@simtlix/simfinity-js 3.5.5 → 3.5.6

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
@@ -6,7 +6,7 @@ MongoDB/Mongoose adapter and compatibility facade for Simfinity. This package ge
6
6
  npm install @simtlix/simfinity-js graphql@^16.11.0 mongoose@^8.24.2
7
7
  ```
8
8
 
9
- `graphql` and `mongoose` are peer dependencies: the facade uses your application's copies, and npm reports an out-of-range version as a peer conflict instead of installing a second copy. The MCP transports (`createMCPServer`, `startStdioMCPServer`, `createHTTPMCPHandler`) need the optional peer `@modelcontextprotocol/sdk@^1.13.0`; install it only when you use them. `generateMCPTools` works without it. The package does not install `graphql-middleware`.
9
+ `graphql` and `mongoose` are peer dependencies: the facade uses your application's copies, and npm reports an out-of-range version as a peer conflict instead of installing a second copy. The MCP transports (`createMCPServer`, `startStdioMCPServer`, `createHTTPMCPHandler`) need the optional peer `@modelcontextprotocol/sdk@^1.31.0`; install it only when you use them. `generateMCPTools` works without it. The package does not install `graphql-middleware`.
10
10
 
11
11
  ```javascript
12
12
  import mongoose from 'mongoose';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simtlix/simfinity-js",
3
- "version": "3.5.5",
3
+ "version": "3.5.6",
4
4
  "description": "MongoDB/Mongoose adapter and compatibility facade for Simfinity GraphQL APIs",
5
5
  "main": "src/index.js",
6
6
  "types": "./types/index.d.ts",
@@ -16,7 +16,7 @@
16
16
  "directory": "packages/mongodb"
17
17
  },
18
18
  "peerDependencies": {
19
- "@modelcontextprotocol/sdk": "^1.13.0",
19
+ "@modelcontextprotocol/sdk": "^1.31.0",
20
20
  "graphql": "^16.11.0",
21
21
  "mongoose": "^8.24.2"
22
22
  },
@@ -29,8 +29,8 @@
29
29
  "owner": "simtlix"
30
30
  },
31
31
  "dependencies": {
32
- "@simtlix/simfinity-core": "3.5.5",
33
- "@simtlix/simfinity-mcp": "3.5.5"
32
+ "@simtlix/simfinity-core": "3.5.6",
33
+ "@simtlix/simfinity-mcp": "3.5.6"
34
34
  },
35
35
  "files": [
36
36
  "src",
@@ -1,4 +1,5 @@
1
1
  import mongoose from 'mongoose';
2
+ import { SimfinityError } from '@simtlix/simfinity-core';
2
3
  import { getListShape, normalizeConnectionField } from '@simtlix/simfinity-core/internal/relation-storage';
3
4
 
4
5
  import { createMongoModel } from './models.js';
@@ -78,6 +79,13 @@ export const createMongoAdapter = (options) => {
78
79
  // self-referencing collections), the child gets a private ObjectId field.
79
80
  const connection = normalizeConnectionField(listShape.itemType, relation.connectionField);
80
81
  if (!connection.graphqlFieldName) {
82
+ // Mongoose drops a stored path named constructor, so the link would never be written.
83
+ if (connection.storageFieldName === 'constructor') {
84
+ throw new SimfinityError(
85
+ `${listShape.itemType.name}.constructor cannot be stored on MongoDB: Mongoose drops a stored path named constructor`,
86
+ 'INVALID_MODEL', 400,
87
+ );
88
+ }
81
89
  addPrivateConnectionField(listShape.itemType.name, connection.storageFieldName);
82
90
  }
83
91
  }
@@ -11,6 +11,7 @@ import {
11
11
  GraphQLString,
12
12
  } from 'graphql';
13
13
  import mongoose from 'mongoose';
14
+ import { SimfinityError } from '@simtlix/simfinity-core';
14
15
  import { getFieldStorageName } from '@simtlix/simfinity-core/internal/relation-storage';
15
16
 
16
17
  const isNonNullOfType = (fieldEntryType, graphQLType) => (
@@ -71,6 +72,21 @@ const enumStorageType = (enumType, fieldName) => {
71
72
  return mongoose.Schema.Types.Mixed;
72
73
  };
73
74
 
75
+ // Mongoose cannot store some fields named like Object.prototype members, because it looks paths up
76
+ // on plain objects that inherit those members. An embedded object so named fails to build, the items
77
+ // of an embedded list so named come back from the driver as raw BSON buffers whose members read as
78
+ // null, and a stored path named `constructor` is dropped from every write. Other scalars, scalar lists and references with
79
+ // these names are stored as usual.
80
+ const unstorableField = (gqlType, fieldEntryName, reason) => new SimfinityError(
81
+ `${gqlType.name}.${fieldEntryName} cannot be stored on MongoDB: ${reason}`, 'INVALID_MODEL', 400,
82
+ );
83
+
84
+ const assertEmbeddedFieldName = (gqlType, fieldEntryName) => {
85
+ if (fieldEntryName in Object.prototype) {
86
+ throw unstorableField(gqlType, fieldEntryName, 'embedded fields cannot be named like Object.prototype members');
87
+ }
88
+ };
89
+
74
90
  const generateSchemaDefinition = (gqlType, nested = false) => {
75
91
  const argTypes = gqlType.getFields();
76
92
  const schemaArg = {};
@@ -93,6 +109,7 @@ const generateSchemaDefinition = (gqlType, nested = false) => {
93
109
  if (!fieldEntry.extensions.relation.embedded) {
94
110
  schemaArg[getFieldStorageName(fieldEntryName, fieldEntry)] = mongoose.Schema.Types.ObjectId;
95
111
  } else {
112
+ assertEmbeddedFieldName(gqlType, fieldEntryName);
96
113
  const entryType = unwrapNonNull(type);
97
114
  if (entryType === gqlType) {
98
115
  throw new Error('A type cannot have a field of its same type and embedded');
@@ -108,6 +125,7 @@ const generateSchemaDefinition = (gqlType, nested = false) => {
108
125
  const itemType = getListItemType(type);
109
126
  if (fieldEntry.extensions && fieldEntry.extensions.relation) {
110
127
  if (fieldEntry.extensions.relation.embedded) {
128
+ assertEmbeddedFieldName(gqlType, fieldEntryName);
111
129
  if (itemType === gqlType) {
112
130
  throw new Error('A type cannot have a field of its same type and embedded');
113
131
  }
@@ -129,6 +147,11 @@ const generateSchemaDefinition = (gqlType, nested = false) => {
129
147
  } else if (isGraphQLisoDate(getEffectiveTypeName(unwrapNonNull(type)))) {
130
148
  schemaArg[fieldEntryName] = Date;
131
149
  }
150
+
151
+ // Whichever stored field takes the `constructor` path, also a reference whose connectionField it is.
152
+ if (Object.hasOwn(schemaArg, 'constructor')) {
153
+ throw unstorableField(gqlType, fieldEntryName, 'Mongoose drops a stored path named constructor');
154
+ }
132
155
  }
133
156
 
134
157
  // In a nested definition, Mongoose reads a `type` key as the type of the whole embedded path.