@mongoosejs/studio 0.5.6 → 0.5.7

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/README.md CHANGED
@@ -1,14 +1,14 @@
1
1
  # Mongoose Studio
2
2
 
3
- A sleek, powerful MongoDB UI with built-in dashboarding and auth, seamlessly integrated with your Express, Vercel, or Netlify app.
3
+ An AI-Powered Data Workspace for MongoDB: Turn your MongoDB data into dashboards, maps, and interactive workflows - powered by your Mongoose models.
4
4
 
5
5
  ![NPM Version](https://img.shields.io/npm/v/@mongoosejs/studio)
6
6
 
7
7
  ## Getting Started
8
8
 
9
9
  Mongoose Studio is meant to run as a [sidecar](https://learn.microsoft.com/en-us/azure/architecture/patterns/sidecar) to your Node.js application, using the same Mongoose connection config.
10
- If your app runs on `acme.app`, Studio will be on `acme.app/studio` or whichever path you prefer.
11
- For local dev, if your app runs on `localhost:3000`, Studio will be on `localhost:3000/studio`.
10
+ If your app runs on `acme.app`, Mongoose Studio will be on `acme.app/studio` or whichever path you prefer.
11
+ For local dev, if your app runs on `localhost:3000`, Mongoose Studio will be on `localhost:3000/studio`.
12
12
 
13
13
  By default, Mongoose Studio does **not** provide any authentication or authorization.
14
14
  You can use Mongoose Studio for free for local development, but we recommend [Mongoose Studio Pro](https://studio.mongoosejs.io/#pricing) for when you want to go into production.
@@ -85,6 +85,38 @@ const handler = studio(
85
85
  export default handler;
86
86
  ```
87
87
 
88
+ ### Nest.js
89
+
90
+ Add `MongooseStudioModule` to your app module as follows.
91
+ Use `getConnectionToken()` so Mongoose Studio uses the same connection that `MongooseModule.forRoot()` creates.
92
+
93
+ ```typescript
94
+ import { Module } from '@nestjs/common';
95
+ import { MongooseModule, getConnectionToken } from '@nestjs/mongoose';
96
+ import { MongooseStudioModule } from '@mongoosejs/studio/nest';
97
+
98
+ @Module({
99
+ imports: [
100
+ MongooseModule.forRoot(process.env.MONGODB_URI),
101
+ MongooseStudioModule.forRoot({
102
+ connectionToken: getConnectionToken(),
103
+ apiKey: process.env.MONGOOSE_STUDIO_API_KEY // optional
104
+ })
105
+ ]
106
+ })
107
+ export class AppModule {}
108
+ ```
109
+
110
+ With this setup, Mongoose Studio is available at `/studio`.
111
+ If you mount Mongoose Studio on a different path, update the `path` option as follows.
112
+
113
+ ```typescript
114
+ MongooseStudioModule.forRoot({
115
+ path: '/__studio', // Serve on `/__studio` rather than `/studio`
116
+ connectionToken: getConnectionToken()
117
+ })
118
+ ```
119
+
88
120
  ### Netlify
89
121
 
90
122
  [Here is a full example of how to add Mongoose Studio to a Netlify repo](https://github.com/mongoosejs/studio.mongoosejs.io/commit/8b02ea367c8a1b7b4bcab290708f57d58f08210b).
package/express.js CHANGED
@@ -9,6 +9,8 @@ const normalizeBindIPOption = require('./backend/helpers/normalizeBindIPOption')
9
9
  const { toRoute, objectRouter } = require('extrovert');
10
10
  const { defaultMothershipURL } = require('./constants');
11
11
 
12
+ const jsonParser = express.json();
13
+
12
14
  module.exports = async function mongooseStudioExpressApp(apiUrl, conn, options) {
13
15
  const router = express.Router();
14
16
  options = options ? { changeStream: true, ...options } : { changeStream: true };
@@ -95,7 +97,12 @@ module.exports = async function mongooseStudioExpressApp(apiUrl, conn, options)
95
97
  return res.status(500).json({ message: err.message });
96
98
  });
97
99
  },
98
- express.json(),
100
+ function parseJson(req, res, next) {
101
+ if (req.body !== undefined || req.readable === false) {
102
+ return next();
103
+ }
104
+ return jsonParser(req, res, next);
105
+ },
99
106
  objectRouter(backend, toRoute)
100
107
  );
101
108
 
@@ -12699,7 +12699,7 @@ __webpack_require__.r(__webpack_exports__);
12699
12699
  /* harmony export */ });
12700
12700
  /* harmony import */ var _vue_shared__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @vue/shared */ "./node_modules/@vue/shared/dist/shared.esm-bundler.js");
12701
12701
  /**
12702
- * @vue/reactivity v3.5.35
12702
+ * @vue/reactivity v3.5.38
12703
12703
  * (c) 2018-present Yuxi (Evan) You and Vue contributors
12704
12704
  * @license MIT
12705
12705
  **/
@@ -14596,8 +14596,9 @@ function watch(source, cb, options = _vue_shared__WEBPACK_IMPORTED_MODULE_0__.EM
14596
14596
  if (once && cb) {
14597
14597
  const _cb = cb;
14598
14598
  cb = (...args) => {
14599
- _cb(...args);
14599
+ const res = _cb(...args);
14600
14600
  watchHandle();
14601
+ return res;
14601
14602
  };
14602
14603
  }
14603
14604
  let oldValue = isMultiSource ? new Array(source.length).fill(INITIAL_WATCHER_VALUE) : INITIAL_WATCHER_VALUE;
@@ -14607,7 +14608,7 @@ function watch(source, cb, options = _vue_shared__WEBPACK_IMPORTED_MODULE_0__.EM
14607
14608
  }
14608
14609
  if (cb) {
14609
14610
  const newValue = effect.run();
14610
- if (deep || forceTrigger || (isMultiSource ? newValue.some((v, i) => (0,_vue_shared__WEBPACK_IMPORTED_MODULE_0__.hasChanged)(v, oldValue[i])) : (0,_vue_shared__WEBPACK_IMPORTED_MODULE_0__.hasChanged)(newValue, oldValue))) {
14611
+ if (immediateFirstRun || deep || forceTrigger || (isMultiSource ? newValue.some((v, i) => (0,_vue_shared__WEBPACK_IMPORTED_MODULE_0__.hasChanged)(v, oldValue[i])) : (0,_vue_shared__WEBPACK_IMPORTED_MODULE_0__.hasChanged)(newValue, oldValue))) {
14611
14612
  if (cleanup) {
14612
14613
  cleanup();
14613
14614
  }
@@ -14867,7 +14868,7 @@ __webpack_require__.r(__webpack_exports__);
14867
14868
  /* harmony import */ var _vue_reactivity__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @vue/reactivity */ "./node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js");
14868
14869
  /* harmony import */ var _vue_shared__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @vue/shared */ "./node_modules/@vue/shared/dist/shared.esm-bundler.js");
14869
14870
  /**
14870
- * @vue/runtime-core v3.5.35
14871
+ * @vue/runtime-core v3.5.38
14871
14872
  * (c) 2018-present Yuxi (Evan) You and Vue contributors
14872
14873
  * @license MIT
14873
14874
  **/
@@ -17599,13 +17600,21 @@ function defineAsyncComponent(source) {
17599
17600
  const loaded = (0,_vue_reactivity__WEBPACK_IMPORTED_MODULE_0__.ref)(false);
17600
17601
  const error = (0,_vue_reactivity__WEBPACK_IMPORTED_MODULE_0__.ref)();
17601
17602
  const delayed = (0,_vue_reactivity__WEBPACK_IMPORTED_MODULE_0__.ref)(!!delay);
17603
+ let timeoutTimer;
17604
+ let delayTimer;
17605
+ onUnmounted(() => {
17606
+ if (timeoutTimer != null) clearTimeout(timeoutTimer);
17607
+ if (delayTimer != null) clearTimeout(delayTimer);
17608
+ });
17602
17609
  if (delay) {
17603
- setTimeout(() => {
17610
+ delayTimer = setTimeout(() => {
17611
+ if (instance.isUnmounted) return;
17604
17612
  delayed.value = false;
17605
17613
  }, delay);
17606
17614
  }
17607
17615
  if (timeout != null) {
17608
- setTimeout(() => {
17616
+ timeoutTimer = setTimeout(() => {
17617
+ if (instance.isUnmounted) return;
17609
17618
  if (!loaded.value && !error.value) {
17610
17619
  const err = new Error(
17611
17620
  `Async component timed out after ${timeout}ms.`
@@ -17616,11 +17625,16 @@ function defineAsyncComponent(source) {
17616
17625
  }, timeout);
17617
17626
  }
17618
17627
  load().then(() => {
17628
+ if (instance.isUnmounted) return;
17619
17629
  loaded.value = true;
17620
17630
  if (instance.parent && isKeepAlive(instance.parent.vnode)) {
17621
17631
  instance.parent.update();
17622
17632
  }
17623
17633
  }).catch((err) => {
17634
+ if (instance.isUnmounted) {
17635
+ pendingRequest = null;
17636
+ return;
17637
+ }
17624
17638
  onError(err);
17625
17639
  error.value = err;
17626
17640
  });
@@ -19202,13 +19216,20 @@ function useModel(props, name, options = _vue_shared__WEBPACK_IMPORTED_MODULE_1_
19202
19216
  return;
19203
19217
  }
19204
19218
  const rawProps = i.vnode.props;
19205
- if (!(rawProps && // check if parent has passed v-model
19206
- (name in rawProps || camelizedName in rawProps || hyphenatedName in rawProps) && (`onUpdate:${name}` in rawProps || `onUpdate:${camelizedName}` in rawProps || `onUpdate:${hyphenatedName}` in rawProps))) {
19219
+ const hasVModel = !!(rawProps && // check if parent has passed v-model
19220
+ (name in rawProps || camelizedName in rawProps || hyphenatedName in rawProps) && (`onUpdate:${name}` in rawProps || `onUpdate:${camelizedName}` in rawProps || `onUpdate:${hyphenatedName}` in rawProps));
19221
+ if (!hasVModel) {
19207
19222
  localValue = value;
19208
19223
  trigger();
19209
19224
  }
19210
19225
  i.emit(`update:${name}`, emittedValue);
19211
- if ((0,_vue_shared__WEBPACK_IMPORTED_MODULE_1__.hasChanged)(value, emittedValue) && (0,_vue_shared__WEBPACK_IMPORTED_MODULE_1__.hasChanged)(value, prevSetValue) && !(0,_vue_shared__WEBPACK_IMPORTED_MODULE_1__.hasChanged)(emittedValue, prevEmittedValue)) {
19226
+ if ((0,_vue_shared__WEBPACK_IMPORTED_MODULE_1__.hasChanged)(value, prevSetValue) && ((0,_vue_shared__WEBPACK_IMPORTED_MODULE_1__.hasChanged)(value, emittedValue) && !(0,_vue_shared__WEBPACK_IMPORTED_MODULE_1__.hasChanged)(emittedValue, prevEmittedValue) || // #13524: browsers differ in when they flush microtasks between
19227
+ // event listeners. If a v-model listener emits an intermediate value
19228
+ // and a following listener restores the model to its previous prop
19229
+ // value before parent updates are flushed, the parent render can be
19230
+ // deduped as having no prop change. Force a local update so DOM state
19231
+ // such as an input's value is synchronized back to the current model.
19232
+ hasVModel && prevSetValue !== _vue_shared__WEBPACK_IMPORTED_MODULE_1__.EMPTY_OBJ && !(0,_vue_shared__WEBPACK_IMPORTED_MODULE_1__.hasChanged)(emittedValue, localValue))) {
19212
19233
  trigger();
19213
19234
  }
19214
19235
  prevSetValue = value;
@@ -23470,7 +23491,7 @@ function isMemoSame(cached, memo) {
23470
23491
  return true;
23471
23492
  }
23472
23493
 
23473
- const version = "3.5.35";
23494
+ const version = "3.5.38";
23474
23495
  const warn = true ? warn$1 : 0;
23475
23496
  const ErrorTypeStrings = ErrorTypeStrings$1 ;
23476
23497
  const devtools = true ? devtools$1 : 0;
@@ -23681,7 +23702,7 @@ __webpack_require__.r(__webpack_exports__);
23681
23702
  /* harmony import */ var _vue_runtime_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @vue/runtime-core */ "./node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js");
23682
23703
  /* harmony import */ var _vue_runtime_core__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @vue/shared */ "./node_modules/@vue/shared/dist/shared.esm-bundler.js");
23683
23704
  /**
23684
- * @vue/runtime-dom v3.5.35
23705
+ * @vue/runtime-dom v3.5.38
23685
23706
  * (c) 2018-present Yuxi (Evan) You and Vue contributors
23686
23707
  * @license MIT
23687
23708
  **/
@@ -25143,7 +25164,8 @@ const TransitionGroupImpl = /* @__PURE__ */ decorate({
25143
25164
  if (children) {
25144
25165
  for (let i = 0; i < children.length; i++) {
25145
25166
  const child = children[i];
25146
- if (child.el && child.el instanceof Element) {
25167
+ if (child.el && child.el instanceof Element && // Hidden v-show nodes have no previous layout box to animate from.
25168
+ !child.el[vShowHidden]) {
25147
25169
  prevChildren.push(child);
25148
25170
  (0,_vue_runtime_core__WEBPACK_IMPORTED_MODULE_0__.setTransitionHooks)(
25149
25171
  child,
@@ -25779,7 +25801,7 @@ __webpack_require__.r(__webpack_exports__);
25779
25801
  /* harmony export */ toTypeString: () => (/* binding */ toTypeString)
25780
25802
  /* harmony export */ });
25781
25803
  /**
25782
- * @vue/shared v3.5.35
25804
+ * @vue/shared v3.5.38
25783
25805
  * (c) 2018-present Yuxi (Evan) You and Vue contributors
25784
25806
  * @license MIT
25785
25807
  **/
@@ -51610,7 +51632,7 @@ __webpack_require__.r(__webpack_exports__);
51610
51632
  /* harmony import */ var _vue_runtime_dom__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @vue/runtime-dom */ "./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js");
51611
51633
  /* harmony import */ var _vue_runtime_dom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @vue/runtime-dom */ "./node_modules/@vue/runtime-dom/dist/runtime-dom.esm-bundler.js");
51612
51634
  /**
51613
- * vue v3.5.35
51635
+ * vue v3.5.38
51614
51636
  * (c) 2018-present Yuxi (Evan) You and Vue contributors
51615
51637
  * @license MIT
51616
51638
  **/
@@ -64953,7 +64975,7 @@ var src_default = VueToastificationPlugin;
64953
64975
  (module) {
64954
64976
 
64955
64977
  "use strict";
64956
- module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.5.6","description":"AI-Powered Data Workspace for MongoDB: Turn your MongoDB data into dashboards, maps, and interactive workflows - powered by your Mongoose models and AI.","homepage":"https://mongoosestudio.app/","repository":{"type":"git","url":"https://github.com/mongoosejs/studio"},"license":"Apache-2.0","dependencies":{"@ai-sdk/anthropic":"2.x","@ai-sdk/google":"2.x","@ai-sdk/openai":"2.x","ace-builds":"^1.43.6","ai":"5.x","archetype":"0.13.1","csv-stringify":"6.3.0","ejson":"^2.2.3","extrovert":"^0.2.0","marked":"15.0.12","mpath":"^0.9.0","node-inspect-extracted":"3.x","regexp.escape":"^2.0.1","tailwindcss":"3.4.0","time-commando":"1.0.1","typescript":"^6.0.2","vue":"3.x","vue-toastification":"^2.0.0-rc.5","webpack":"5.x","xss":"^1.0.15"},"peerDependencies":{"mongoose":"7.x || 8.x || ^9.0.0"},"optionalPeerDependencies":{"@mongoosejs/task":"0.5.x || 0.6.x"},"devDependencies":{"@masteringjs/eslint-config":"0.1.1","axios":"1.2.2","dedent":"^1.6.0","eslint":"9.30.0","express":"4.x","mocha":"10.2.0","mongodb-memory-server":"^11.0.1","mongoose":"9.x","sinon":"^21.0.1"},"scripts":{"lint":"eslint .","seed":"node seed/index.js","start":"node ./local.js","tailwind":"tailwindcss -o ./frontend/public/tw.css","tailwind:watch":"tailwindcss -o ./frontend/public/tw.css --watch","test":"mocha test/*.test.js","test:frontend":"mocha test/frontend/*.test.js","eval:agent":"node eval/agent/run.js"}}');
64978
+ module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.5.7","description":"AI-Powered Data Workspace for MongoDB: Turn your MongoDB data into dashboards, maps, and interactive workflows - powered by your Mongoose models and AI.","homepage":"https://mongoosestudio.app/","repository":{"type":"git","url":"https://github.com/mongoosejs/studio"},"license":"Apache-2.0","dependencies":{"@ai-sdk/anthropic":"2.x","@ai-sdk/google":"2.x","@ai-sdk/openai":"2.x","ace-builds":"^1.43.6","ai":"5.x","archetype":"0.13.1","csv-stringify":"6.3.0","ejson":"^2.2.3","extrovert":"^0.2.0","marked":"15.0.12","mpath":"^0.9.0","node-inspect-extracted":"3.x","regexp.escape":"^2.0.1","tailwindcss":"3.4.0","time-commando":"1.0.1","typescript":"^6.0.2","vue":"3.x","vue-toastification":"^2.0.0-rc.5","webpack":"5.x","xss":"^1.0.15"},"peerDependencies":{"@nestjs/common":"10.x || 11.x","mongoose":"7.x || 8.x || ^9.0.0"},"peerDependenciesMeta":{"@nestjs/common":{"optional":true}},"optionalPeerDependencies":{"@mongoosejs/task":"0.5.x || 0.6.x"},"devDependencies":{"@masteringjs/eslint-config":"0.1.1","axios":"1.2.2","dedent":"^1.6.0","eslint":"9.30.0","@nestjs/common":"^11.1.9","express":"4.x","mocha":"10.2.0","mongodb-memory-server":"^11.0.1","mongoose":"9.x","sinon":"^21.0.1"},"scripts":{"lint":"eslint .","seed":"node seed/index.js","start":"node ./local.js","tailwind":"tailwindcss -o ./frontend/public/tw.css","tailwind:watch":"tailwindcss -o ./frontend/public/tw.css --watch","test":"mocha test/*.test.js","test:frontend":"mocha test/frontend/*.test.js","eval:agent":"node eval/agent/run.js"}}');
64957
64979
 
64958
64980
  /***/ }
64959
64981
 
package/nest.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ declare module '@mongoosejs/studio/nest' {
2
+ import { DynamicModule, MiddlewareConsumer, NestModule } from '@nestjs/common';
3
+ import { Connection, Mongoose } from 'mongoose';
4
+
5
+ export interface MongooseStudioModuleOptions {
6
+ path?: string;
7
+ apiPath?: string;
8
+ connection?: Connection | Mongoose;
9
+ connectionToken?: string | symbol | Function;
10
+ apiKey?: string;
11
+ bindIp?: string | string[] | null;
12
+ [key: string]: any;
13
+ }
14
+
15
+ export interface MongooseStudioModuleAsyncOptions {
16
+ imports?: any[];
17
+ inject?: any[];
18
+ connectionToken?: string | symbol | Function;
19
+ useFactory: (...args: any[]) => Promise<MongooseStudioModuleOptions> | MongooseStudioModuleOptions;
20
+ }
21
+
22
+ export class MongooseStudioModule implements NestModule {
23
+ configure(consumer: MiddlewareConsumer): void;
24
+ static forRoot(options?: MongooseStudioModuleOptions): DynamicModule;
25
+ static forRootAsync(options: MongooseStudioModuleAsyncOptions): DynamicModule;
26
+ }
27
+ }
package/nest.js ADDED
@@ -0,0 +1,134 @@
1
+ 'use strict';
2
+
3
+ const { Inject, Module, RequestMethod } = require('@nestjs/common');
4
+ const mongoose = require('mongoose');
5
+ const createExpressRouter = require('./express');
6
+
7
+ const MONGOOSE_STUDIO_OPTIONS = Symbol('MONGOOSE_STUDIO_OPTIONS');
8
+ const MONGOOSE_STUDIO_CONNECTION = Symbol('MONGOOSE_STUDIO_CONNECTION');
9
+ const MONGOOSE_STUDIO_EXPRESS_ROUTER = Symbol('MONGOOSE_STUDIO_EXPRESS_ROUTER');
10
+
11
+ class MongooseStudioModule {
12
+ constructor(options, expressRouter) {
13
+ this.options = options || {};
14
+ this.expressRouter = expressRouter;
15
+ }
16
+
17
+ configure(consumer) {
18
+ const mountPath = this.options.path || '/studio';
19
+
20
+ consumer
21
+ .apply((req, res, next) => {
22
+ if (!isMountedStudioRequest(req, mountPath)) {
23
+ return next();
24
+ }
25
+
26
+ const originalUrl = req.url;
27
+ req.url = req.url.slice(normalizeExpressMountPath(mountPath).length) || '/';
28
+ try {
29
+ return this.expressRouter(req, res, err => {
30
+ req.url = originalUrl;
31
+ next(err);
32
+ });
33
+ } finally {
34
+ req.url = originalUrl;
35
+ }
36
+ })
37
+ .forRoutes({
38
+ path: '*path',
39
+ method: RequestMethod.ALL
40
+ });
41
+ }
42
+
43
+ static forRoot(options) {
44
+ options = options || {};
45
+ const providers = [
46
+ { provide: MONGOOSE_STUDIO_OPTIONS, useValue: options },
47
+ createExpressRouterProvider()
48
+ ];
49
+
50
+ if (options.connectionToken) {
51
+ providers.push({ provide: MONGOOSE_STUDIO_CONNECTION, useExisting: options.connectionToken });
52
+ } else if (options.connection) {
53
+ providers.push({ provide: MONGOOSE_STUDIO_CONNECTION, useValue: options.connection });
54
+ } else {
55
+ providers.push({ provide: MONGOOSE_STUDIO_CONNECTION, useValue: mongoose });
56
+ }
57
+
58
+ return {
59
+ module: MongooseStudioModule,
60
+ providers,
61
+ exports: providers
62
+ };
63
+ }
64
+
65
+ static forRootAsync(options) {
66
+ options = options || {};
67
+ const providers = [
68
+ {
69
+ provide: MONGOOSE_STUDIO_OPTIONS,
70
+ useFactory: options.useFactory,
71
+ inject: options.inject || []
72
+ },
73
+ createExpressRouterProvider()
74
+ ];
75
+
76
+ if (options.connectionToken) {
77
+ providers.push({ provide: MONGOOSE_STUDIO_CONNECTION, useExisting: options.connectionToken });
78
+ } else if (options.connection) {
79
+ providers.push({ provide: MONGOOSE_STUDIO_CONNECTION, useValue: options.connection });
80
+ } else {
81
+ providers.push({ provide: MONGOOSE_STUDIO_CONNECTION, useValue: mongoose });
82
+ }
83
+
84
+ return {
85
+ module: MongooseStudioModule,
86
+ imports: options.imports || [],
87
+ providers,
88
+ exports: providers
89
+ };
90
+ }
91
+ }
92
+
93
+ function createExpressRouterProvider() {
94
+ return {
95
+ provide: MONGOOSE_STUDIO_EXPRESS_ROUTER,
96
+ useFactory: async(options, connection) => {
97
+ options = options || {};
98
+ const mountPath = options.path || '/studio';
99
+ const apiPath = options.apiPath || `${mountPath}/api`;
100
+ const studioOptions = { ...options };
101
+ delete studioOptions.path;
102
+ delete studioOptions.apiPath;
103
+ delete studioOptions.connection;
104
+ delete studioOptions.connectionToken;
105
+
106
+ return createExpressRouter(apiPath, options.connection || connection || mongoose, studioOptions);
107
+ },
108
+ inject: [MONGOOSE_STUDIO_OPTIONS, MONGOOSE_STUDIO_CONNECTION]
109
+ };
110
+ }
111
+
112
+ function isMountedStudioRequest(req, mountPath) {
113
+ const normalizedMountPath = normalizeExpressMountPath(mountPath);
114
+ return req.url === normalizedMountPath ||
115
+ req.url.startsWith(`${normalizedMountPath}/`) ||
116
+ req.url.startsWith(`${normalizedMountPath}?`) ||
117
+ req.url.startsWith(`${normalizedMountPath}#`);
118
+ }
119
+
120
+ function normalizeExpressMountPath(path) {
121
+ path = `/${path.replace(/^\/+/, '').replace(/\/+$/, '')}`;
122
+ return path === '/' ? '' : path;
123
+ }
124
+
125
+ Inject(MONGOOSE_STUDIO_OPTIONS)(MongooseStudioModule, undefined, 0);
126
+ Inject(MONGOOSE_STUDIO_EXPRESS_ROUTER)(MongooseStudioModule, undefined, 1);
127
+ Module({})(MongooseStudioModule);
128
+
129
+ module.exports = {
130
+ MONGOOSE_STUDIO_CONNECTION,
131
+ MONGOOSE_STUDIO_EXPRESS_ROUTER,
132
+ MONGOOSE_STUDIO_OPTIONS,
133
+ MongooseStudioModule
134
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mongoosejs/studio",
3
- "version": "0.5.6",
3
+ "version": "0.5.7",
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": {
@@ -31,8 +31,14 @@
31
31
  "xss": "^1.0.15"
32
32
  },
33
33
  "peerDependencies": {
34
+ "@nestjs/common": "10.x || 11.x",
34
35
  "mongoose": "7.x || 8.x || ^9.0.0"
35
36
  },
37
+ "peerDependenciesMeta": {
38
+ "@nestjs/common": {
39
+ "optional": true
40
+ }
41
+ },
36
42
  "optionalPeerDependencies": {
37
43
  "@mongoosejs/task": "0.5.x || 0.6.x"
38
44
  },
@@ -41,6 +47,7 @@
41
47
  "axios": "1.2.2",
42
48
  "dedent": "^1.6.0",
43
49
  "eslint": "9.30.0",
50
+ "@nestjs/common": "^11.1.9",
44
51
  "express": "4.x",
45
52
  "mocha": "10.2.0",
46
53
  "mongodb-memory-server": "^11.0.1",