@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 +1 -1
- package/package.json +4 -4
- package/src/mongo/adapter.js +8 -0
- package/src/mongo/models.js +23 -0
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.
|
|
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.
|
|
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.
|
|
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.
|
|
33
|
-
"@simtlix/simfinity-mcp": "3.5.
|
|
32
|
+
"@simtlix/simfinity-core": "3.5.6",
|
|
33
|
+
"@simtlix/simfinity-mcp": "3.5.6"
|
|
34
34
|
},
|
|
35
35
|
"files": [
|
|
36
36
|
"src",
|
package/src/mongo/adapter.js
CHANGED
|
@@ -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
|
}
|
package/src/mongo/models.js
CHANGED
|
@@ -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.
|