@simtlix/simfinity-js 3.0.1 → 3.2.0
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 +150 -83
- package/package.json +17 -4
- package/src/auth/errors.js +2 -44
- package/src/auth/expressions.js +2 -273
- package/src/auth/index.js +3 -390
- package/src/auth/register-mongo-object-id.js +7 -0
- package/src/auth/rules.js +3 -273
- package/src/const/QLOperator.js +1 -39
- package/src/const/QLSort.js +1 -28
- package/src/const/QLValue.js +1 -39
- package/src/errors/internal-server.error.js +1 -11
- package/src/errors/simfinity.error.js +1 -15
- package/src/index.js +34 -1991
- package/src/mcp.js +2 -1377
- package/src/mongo/adapter.js +111 -0
- package/src/mongo/models.js +160 -0
- package/src/mongo/queries.js +570 -0
- package/src/mongo/transactions.js +81 -0
- package/src/plugins.js +2 -53
- package/src/scalars.js +2 -188
- package/src/validators.js +2 -250
- package/types/index.d.ts +65 -480
package/README.md
CHANGED
|
@@ -1,11 +1,27 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
1
3
|
# Simfinity.js
|
|
2
4
|
|
|
3
|
-
A
|
|
5
|
+
A Node.js framework that turns GraphQL object types into generated queries, mutations, relationships, and database storage. Use the established MongoDB/Mongoose facade or the PostgreSQL 15+ facade with real tables and foreign keys.
|
|
6
|
+
|
|
7
|
+
Read the [documentation website](https://simtlix.github.io/simfinity.js/): start with the [quick start](https://simtlix.github.io/simfinity.js/guide/getting-started.html), explore the [guides](https://simtlix.github.io/simfinity.js/guide/schema.html), or consult the [API reference](https://simtlix.github.io/simfinity.js/reference/api.html). The website source is in [`docs/`](docs/).
|
|
8
|
+
|
|
9
|
+
Run the documentation website locally with Node.js 22+:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm run docs:install
|
|
13
|
+
npm run docs:dev
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
For builds and hosting, see the [website maintainer guide](docs/.vitepress/README.md). The website documents the current source; some older examples later in this README retain historical conventions.
|
|
17
|
+
|
|
18
|
+
> **Documentation for both databases:** The [public website](https://simtlix.github.io/simfinity.js/guide/databases.html) now covers MongoDB and PostgreSQL, including shared APIs, relationships, generated FKs, scopes, and MCP. Both adapters are available in **v3.2.0** on npm. Follow the quick starts for installation, or download the runnable starters and verified release archives.
|
|
4
19
|
|
|
5
20
|
## 📑 Table of Contents
|
|
6
21
|
|
|
7
22
|
- [Features](#-features)
|
|
8
23
|
- [Installation](#-installation)
|
|
24
|
+
- [PostgreSQL support](#postgresql-support)
|
|
9
25
|
- [Quick Start](#-quick-start)
|
|
10
26
|
- [Core Concepts](#-core-concepts)
|
|
11
27
|
- [Connecting Models](#connecting-models)
|
|
@@ -74,8 +90,8 @@ A powerful Node.js framework that automatically generates GraphQL schemas from y
|
|
|
74
90
|
## ✨ Features
|
|
75
91
|
|
|
76
92
|
- **Automatic Schema Generation**: Define your object model, and Simfinity.js generates all queries and mutations
|
|
77
|
-
- **MongoDB
|
|
78
|
-
- **Powerful Querying**:
|
|
93
|
+
- **MongoDB or PostgreSQL**: Choose the database facade once during application startup
|
|
94
|
+
- **Powerful Querying**: Typed filters, nested paths, pagination, sorting, and aggregations across the supported contract
|
|
79
95
|
- **Aggregation Queries**: Built-in support for GROUP BY queries with aggregation operations (SUM, COUNT, AVG, MIN, MAX)
|
|
80
96
|
- **Auto-Generated Resolvers**: Automatically generates resolve methods for relationship fields
|
|
81
97
|
- **Automatic Index Creation**: Automatically creates MongoDB indexes for all ObjectId fields, including nested embedded objects and relationship fields
|
|
@@ -89,101 +105,86 @@ A powerful Node.js framework that automatically generates GraphQL schemas from y
|
|
|
89
105
|
## 📦 Installation
|
|
90
106
|
|
|
91
107
|
```bash
|
|
92
|
-
npm install mongoose graphql @simtlix/simfinity-js
|
|
108
|
+
npm install mongoose@^8.16.2 graphql@^16.11.0 @simtlix/simfinity-js@3.2.0
|
|
93
109
|
```
|
|
94
110
|
|
|
95
111
|
**Prerequisites**: Simfinity.js requires `mongoose` and `graphql` as peer dependencies.
|
|
96
112
|
|
|
97
|
-
##
|
|
98
|
-
|
|
99
|
-
### 1. Basic Setup
|
|
113
|
+
## PostgreSQL support
|
|
100
114
|
|
|
101
|
-
|
|
102
|
-
const express = require('express');
|
|
103
|
-
const { graphqlHTTP } = require('express-graphql');
|
|
104
|
-
const mongoose = require('mongoose');
|
|
105
|
-
const simfinity = require('@simtlix/simfinity-js');
|
|
115
|
+
Version 3.2.0 releases `@simtlix/simfinity-core`, `@simtlix/simfinity-mcp`, `@simtlix/simfinity-postgres`, and the root MongoDB facade in lockstep. Install the selected adapter from npm; shared dependencies resolve automatically. PostgreSQL runs the shared GraphQL query/mutation engine, including scopes, controllers, validators, state transitions and nested writes. It generates and validates tables, indexes, and **real foreign keys**, including inverse relations, explicit many-to-many linking entities, and references inside embedded objects. PostgreSQL installation does not pull Mongoose, MongoDB, or MCP dependencies.
|
|
106
116
|
|
|
107
|
-
|
|
108
|
-
mongoose.connect('mongodb://localhost:27017/bookstore', {
|
|
109
|
-
useNewUrlParser: true,
|
|
110
|
-
useUnifiedTopology: true,
|
|
111
|
-
});
|
|
117
|
+
Enum filters resolve member names first, then declared internal values by strict equality, on both backends. For example, with `ONE: { value: 'TWO' }` and `TWO: { value: 'two' }`, filter `"TWO"` selects member `TWO`. Numeric internal values require numbers, not numeric strings. This applies to scalar lists, embedded/reference leaves and state filters across EQ, NE, LT, LTE, GT, GTE, BTW, IN and NIN. LIKE accepts string fields only. PostgreSQL writes and state guards continue to use internal enum values.
|
|
112
118
|
|
|
113
|
-
|
|
114
|
-
```
|
|
119
|
+
The shared runtime preserves the v3.1 contract: generated relationships run target middleware/scopes with protected identity and parent filters; nested mutations enforce child middleware and persisted ownership; standalone `saveObject()` wraps the complete workflow in a transaction. Accepted empty strings are preserved. Both facades expose `configureQueryLimits()` for bounded pagination.
|
|
115
120
|
|
|
116
|
-
|
|
121
|
+
Choose the backend at application setup. The existing package continues to use MongoDB; PostgreSQL uses `createPostgres({ pool, schema })`, the same `connect(null, Type, ...)`/`createSchema()` signatures, and an awaited `initializeDatabase()` before serving requests. See the canonical [PostgreSQL quick start](docs/guide/postgresql.md), detailed [storage reference](docs/postgresql.md), and [compatibility contract](docs/compatibility.md) before adopting this version.
|
|
117
122
|
|
|
118
|
-
|
|
119
|
-
const { GraphQLObjectType, GraphQLString, GraphQLNonNull, GraphQLID } = require('graphql');
|
|
123
|
+
Both database facades expose the same `auth`, `validators`, `scalars`, and `plugins` helper objects. PostgreSQL keeps MCP optional; install the database-independent integration and its transport SDK only when needed:
|
|
120
124
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
fields: () => ({
|
|
124
|
-
id: { type: new GraphQLNonNull(GraphQLID) },
|
|
125
|
-
title: { type: new GraphQLNonNull(GraphQLString) },
|
|
126
|
-
author: { type: GraphQLString },
|
|
127
|
-
}),
|
|
128
|
-
});
|
|
125
|
+
```sh
|
|
126
|
+
npm install @simtlix/simfinity-mcp@3.2.0 @modelcontextprotocol/sdk@^1.13.0
|
|
129
127
|
```
|
|
130
128
|
|
|
131
|
-
|
|
129
|
+
Import `generateMCPTools`, `createMCPServer`, or the transport helpers from `@simtlix/simfinity-mcp` and pass the schema returned by `createPostgres().createSchema()`.
|
|
132
130
|
|
|
133
|
-
|
|
134
|
-
// Connect the type to Simfinity
|
|
135
|
-
simfinity.connect(null, BookType, 'book', 'books');
|
|
131
|
+
Compatibility tests run the same GraphQL schemas and query corpus against both databases. IDs use UUIDs on PostgreSQL, native model/session APIs differ, and some mappings remain explicitly unsupported, including whole embedded-object uniqueness and whole-object sorting/grouping. Nested embedded scalar-list filters/sorts, ragged array group keys and array aggregate facts now match the supported Mongo query contract. Native embedded writes retain explicit nulls, apply descendant list defaults and minimize empty inline objects; historical Date parameters use UTC. PostgreSQL enforces scalar/list multikey uniqueness within embedded trees through typed owner-key tables, validates JSONB embedded shapes, and checks owned marker/row consistency with deferred database triggers. MongoDB fixes also cover list nullability wrappers, scalar-ID inverse relations, inverse aggregation paths, embedded array replacement after clearing, and isolation of mutation input across transaction retries. Existing API signatures remain unchanged.
|
|
136
132
|
|
|
137
|
-
|
|
138
|
-
|
|
133
|
+
## 🚀 Quick Start
|
|
134
|
+
|
|
135
|
+
The [complete quick start](docs/guide/getting-started.md) includes MongoDB setup, installation, and working create, read, update, and delete operations. Simfinity uses ES modules and named exports.
|
|
136
|
+
|
|
137
|
+
```sh
|
|
138
|
+
npm install @simtlix/simfinity-js graphql@^16.11.0 mongoose@^8.16.2 graphql-yoga@^5
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
Set `"type": "module"` in your application's `package.json`, then create `server.js`:
|
|
142
142
|
|
|
143
143
|
```javascript
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
})
|
|
150
|
-
}));
|
|
144
|
+
import { createServer } from 'node:http';
|
|
145
|
+
import { GraphQLID, GraphQLNonNull, GraphQLObjectType, GraphQLString } from 'graphql';
|
|
146
|
+
import { createYoga } from 'graphql-yoga';
|
|
147
|
+
import mongoose from 'mongoose';
|
|
148
|
+
import * as simfinity from '@simtlix/simfinity-js';
|
|
151
149
|
|
|
152
|
-
|
|
153
|
-
|
|
150
|
+
const SerieType = new GraphQLObjectType({
|
|
151
|
+
name: 'Serie',
|
|
152
|
+
fields: {
|
|
153
|
+
id: { type: GraphQLID },
|
|
154
|
+
name: { type: new GraphQLNonNull(GraphQLString) },
|
|
155
|
+
},
|
|
154
156
|
});
|
|
155
|
-
```
|
|
156
157
|
|
|
157
|
-
|
|
158
|
+
await mongoose.connect(process.env.MONGODB_URI || 'mongodb://127.0.0.1:27017/series?replicaSet=rs0&directConnection=true');
|
|
159
|
+
simfinity.connect(null, SerieType, 'serie', 'series');
|
|
160
|
+
const schema = simfinity.createSchema();
|
|
161
|
+
const yoga = createYoga({ schema });
|
|
162
|
+
createServer(yoga).listen(4000, '127.0.0.1', () => {
|
|
163
|
+
console.log('GraphQL ready at http://127.0.0.1:4000/graphql');
|
|
164
|
+
});
|
|
165
|
+
```
|
|
158
166
|
|
|
159
|
-
|
|
167
|
+
Generated mutations require a MongoDB replica set or sharded cluster. Follow the [local MongoDB setup](docs/guide/getting-started.md#2-start-mongodb), then run `node server.js` and open `http://127.0.0.1:4000/graphql`:
|
|
160
168
|
|
|
161
|
-
**Create a book:**
|
|
162
169
|
```graphql
|
|
163
170
|
mutation {
|
|
164
|
-
|
|
165
|
-
title: "The Hitchhiker's Guide to the Galaxy"
|
|
166
|
-
author: "Douglas Adams"
|
|
167
|
-
}) {
|
|
171
|
+
addserie(input: { name: "The Expanse" }) {
|
|
168
172
|
id
|
|
169
|
-
|
|
170
|
-
author
|
|
173
|
+
name
|
|
171
174
|
}
|
|
172
175
|
}
|
|
173
176
|
```
|
|
174
177
|
|
|
175
|
-
**List all books:**
|
|
176
178
|
```graphql
|
|
177
179
|
query {
|
|
178
|
-
|
|
180
|
+
series {
|
|
179
181
|
id
|
|
180
|
-
|
|
181
|
-
author
|
|
182
|
+
name
|
|
182
183
|
}
|
|
183
184
|
}
|
|
184
185
|
```
|
|
185
186
|
|
|
186
|
-
|
|
187
|
+
For a full working application, see the [Series Sample Project](https://github.com/simtlix/series-sample), with relationships, state machines, controllers, and authorization.
|
|
187
188
|
|
|
188
189
|
## 🔧 Core Concepts
|
|
189
190
|
|
|
@@ -215,6 +216,12 @@ const schema = simfinity.createSchema(
|
|
|
215
216
|
);
|
|
216
217
|
```
|
|
217
218
|
|
|
219
|
+
Importing Simfinity initializes its global `__Field.extensions` introspection field safely whether GraphQL's fields have already been materialized or a schema already exists. Repeated module evaluation with the same GraphQL peer reuses the existing extension field and metadata types. Application field metadata is preserved.
|
|
220
|
+
|
|
221
|
+
The extension remains shared by every schema using that GraphQL peer; Simfinity's type and middleware registries belong to each runtime instance. Schemas constructed after import include `FieldExtensionsType` and `RelationType` in their type maps. Earlier schemas keep their original type maps: ordinary operations and direct metadata selections work, but named fragments on the new metadata types require a schema constructed after import.
|
|
222
|
+
|
|
223
|
+
Schema-cloning tools remain unsupported. In a process that has imported Simfinity, `buildClientSchema()` on a post-import schema's introspection result can also fail with duplicate metadata type names. Use Envelop plugins and in-place resolver wrapping; see the [introspection metadata reference](docs/reference/extensions.md).
|
|
224
|
+
|
|
218
225
|
### Global Configuration
|
|
219
226
|
|
|
220
227
|
```javascript
|
|
@@ -245,8 +252,25 @@ Simfinity automatically generates mutations for each connected type:
|
|
|
245
252
|
// - deleteBook(id: ID): Book
|
|
246
253
|
```
|
|
247
254
|
|
|
255
|
+
Creation inputs retain required scalar, enum, embedded-object, and list fields. Update inputs remove the outer non-null wrapper so fields can be omitted; the entity `id` remains required for `GraphQLID` and `GraphQLID!`. Other ID fields remain optional on update. List item nullability is preserved:
|
|
256
|
+
|
|
257
|
+
| Output field | Create input | Update input |
|
|
258
|
+
| --- | --- | --- |
|
|
259
|
+
| `[String]` | `[String]` | `[String]` |
|
|
260
|
+
| `[String]!` | `[String]!` | `[String]` |
|
|
261
|
+
| `[String!]` | `[String!]` | `[String!]` |
|
|
262
|
+
| `[String!]!` | `[String!]!` | `[String!]` |
|
|
263
|
+
|
|
264
|
+
The same wrapper handling applies to enum lists, supported custom scalar lists, and embedded lists (using their nested input types). Referenced collections keep the `added` / `updated` / `deleted` input object: a required collection requires that object on create, and non-null object items make `added` and `updated` items non-null. Nullable collection-operation items are ignored. Use `deleted` to delete children.
|
|
265
|
+
|
|
266
|
+
Empty strings, `false`, `0`, and empty arrays are persisted after validators accept them. Omitting an update field leaves its value unchanged. Explicit `null` clears a nullable scalar, embedded field, or single-object reference; references clear the actual stored `connectionField`, or the GraphQL field name when no override is configured. Multiple nullable fields can be cleared in one update. An explicit `null` for an originally non-null field leaves its stored value unchanged; use an update validator to reject that input if needed.
|
|
267
|
+
|
|
248
268
|
### Filtering and Querying
|
|
249
269
|
|
|
270
|
+
Every relationship or embedded `terms` entry is combined with AND, including repeated paths such as `age GTE 18` and `age LTE 30`. These conditions remain ANDed with top-level logical groups and scope filters. Filter and list-sort paths must resolve to declared scalar or enum fields. `id` paths refer to the stored `_id`; comparisons use the connected Mongoose model's schema, so supplied string or numeric ID models retain their identifier representation.
|
|
271
|
+
|
|
272
|
+
Filter values must use the field's JSON scalar type. `IN` and `NIN` require flat lists, `BTW` requires exactly two non-null bounds, and `LIKE` requires a string search fragment. Literal objects, nested lists, null list elements, invalid operators, unknown paths, and malformed groups are rejected with a structured 400 error instead of being ignored. Explicit `null` remains supported by `EQ` and `NE`. Enum names or declared internal enum values are converted to the stored representation; state-machine `state` filters preserve stored state names. Date filters convert valid date values without mutating the query input. Validated string scalars support partial `LIKE` searches, and range filters use their base scalar type rather than the field's create/update validation constraints.
|
|
273
|
+
|
|
250
274
|
Query with powerful filtering options:
|
|
251
275
|
|
|
252
276
|
```graphql
|
|
@@ -660,7 +684,7 @@ const BookType = new GraphQLObjectType({
|
|
|
660
684
|
|
|
661
685
|
### Relationship Configuration
|
|
662
686
|
|
|
663
|
-
- `connectionField`: **(Required for collections)** The field
|
|
687
|
+
- `connectionField`: **(Required for referenced collections)** The child field linking back to the parent. For a single-object reference, this optionally overrides the stored ObjectId field; when omitted, model generation, writes, clears, and resolvers use the GraphQL field name.
|
|
664
688
|
- `displayField`: **(Optional)** Field to use for display in UI components
|
|
665
689
|
- `embedded`: **(Optional)** Whether the relation is embedded (default: false)
|
|
666
690
|
|
|
@@ -981,6 +1005,8 @@ mutation {
|
|
|
981
1005
|
}
|
|
982
1006
|
```
|
|
983
1007
|
|
|
1008
|
+
For non-embedded collections, each `added`, `updated`, or `deleted` child runs the target type's global middleware with the request context. The operation and argument shapes match root mutations: `save`/`update` receive `{ input }`, and `delete` receives `{ id }`. Updated and deleted children must already belong to the current parent in the transaction; a missing child raises `NOT_VALID_ID`, and a different parent raises `FORBIDDEN`. Nested updates cannot move a child between parents. A rejection aborts the parent mutation and its child changes.
|
|
1009
|
+
|
|
984
1010
|
## ✅ Validations
|
|
985
1011
|
|
|
986
1012
|
### Declarative Validation Helpers
|
|
@@ -1443,7 +1469,7 @@ const bookController = {
|
|
|
1443
1469
|
onSaved: async (doc, args, session, context) => {
|
|
1444
1470
|
// After saving - doc is a plain object
|
|
1445
1471
|
console.log(`Book saved: ${doc._id}`);
|
|
1446
|
-
//
|
|
1472
|
+
// Use context.user for transactional audit/outbox records on this session
|
|
1447
1473
|
},
|
|
1448
1474
|
|
|
1449
1475
|
onUpdating: async (id, doc, session, context) => {
|
|
@@ -1495,7 +1521,7 @@ simfinity.connect(null, BookType, 'book', 'books', bookController);
|
|
|
1495
1521
|
- `context`: GraphQL context object (includes request info, user data, etc.)
|
|
1496
1522
|
|
|
1497
1523
|
**`onUpdated(doc, session, context)`**:
|
|
1498
|
-
- `doc`: Full updated Mongoose document
|
|
1524
|
+
- `doc`: Full updated Mongoose document, after the parent update and nested collection writes have completed
|
|
1499
1525
|
- `session`: Mongoose session for transaction
|
|
1500
1526
|
- `context`: GraphQL context object (includes request info, user data, etc.)
|
|
1501
1527
|
|
|
@@ -1504,6 +1530,10 @@ simfinity.connect(null, BookType, 'book', 'books', bookController);
|
|
|
1504
1530
|
- `session`: Mongoose session for transaction
|
|
1505
1531
|
- `context`: GraphQL context object (includes request info, user data, etc.)
|
|
1506
1532
|
|
|
1533
|
+
The parent update executes before nested child operations. An update with no matching parent throws `NOT_VALID_ID` (404), without running child operations or `onUpdated`.
|
|
1534
|
+
|
|
1535
|
+
Lifecycle hooks, including `onSaved` and `onUpdated`, run before the transaction commits. They can run again after a transient transaction failure, with up to five retries after the first attempt. Keep additional database writes on the supplied session. For email or webhooks, write an outbox record in that transaction and deliver it after commit; external calls inside hooks cannot be rolled back. An uncertain commit result retries only the commit, up to five times, without repeating hooks. If the result remains uncertain, the error is returned and the database may already have committed; reconcile the outcome before repeating the operation.
|
|
1536
|
+
|
|
1507
1537
|
### Using Context in Controllers
|
|
1508
1538
|
|
|
1509
1539
|
The `context` parameter provides access to the GraphQL request context, which typically includes user information, request metadata, and other application-specific data. This is particularly useful for:
|
|
@@ -1846,6 +1876,8 @@ const EpisodeType = new GraphQLObjectType({
|
|
|
1846
1876
|
- **Modify Args In Place**: Scope functions should modify the `args` object directly
|
|
1847
1877
|
- **Filter Structure**: Use the correct filter structure (`QLFilter` for scalars, `QLTypeFilterExpression` for relations)
|
|
1848
1878
|
- **All Query Operations**: Scope applies to `find`, `aggregate`, and `get_by_id` operations
|
|
1879
|
+
- **Generated Relationships**: Non-embedded single relations run the target type's `get_by_id` middleware and scope; collection relations run its `find` middleware and scope with the same request context. A separate database predicate preserves the referenced ID or parent connection even when filters are changed, including by scope functions. A scoped-out single relation returns `null`; a collection omits scoped-out children.
|
|
1880
|
+
- **Custom Resolvers and Writes**: Existing relationship resolvers remain unchanged. Query scope does not authorize mutations; use global middleware or controller checks for write permissions, including permissions on nested children.
|
|
1849
1881
|
- **Automatic Merging**: For `get_by_id`, the id filter is automatically combined with scope filters
|
|
1850
1882
|
- **Context Access**: Use `context.user`, `context.ip`, or other context properties to determine scope
|
|
1851
1883
|
|
|
@@ -1916,14 +1948,20 @@ const permissions = {
|
|
|
1916
1948
|
|
|
1917
1949
|
**Rule Types:**
|
|
1918
1950
|
- **Function**: `(parent, args, ctx, info) => boolean | void | Promise<boolean | void>`
|
|
1919
|
-
- **
|
|
1920
|
-
- **Policy expression**: JSON AST object (see below)
|
|
1951
|
+
- **Nonempty array of rules**: All rules must pass (AND logic); functions, expressions, and nested nonempty arrays can be mixed
|
|
1952
|
+
- **Policy expression**: JSON AST object or boolean literal (see below)
|
|
1953
|
+
|
|
1954
|
+
Permission maps must be plain objects (including objects with a null prototype). Only their own type and field entries are used. The default policy applies only when an exact field entry and wildcard are both absent. `defaultPolicy` must be exactly `'ALLOW'` or `'DENY'`; omitting it defaults to `'DENY'`.
|
|
1955
|
+
|
|
1956
|
+
`createAuthPlugin`, `createAuthMiddleware`, and `createFieldMiddleware` throw `TypeError` when configured with an invalid rule, empty rule array, malformed policy expression, permission map, or default policy. A configured `null`, `undefined`, or unknown object never falls back to `ALLOW`. Use `allow()` or `true` to grant access explicitly, or omit the entry to use the default policy.
|
|
1921
1957
|
|
|
1922
1958
|
**Rule Semantics:**
|
|
1923
1959
|
- `return true` or `return void` → allow
|
|
1924
1960
|
- `return false` → deny
|
|
1925
1961
|
- `throw Error` → deny with error
|
|
1926
1962
|
|
|
1963
|
+
Only `true` and `undefined` allow access. Other results, including `null`, `0`, an empty string, objects, and other truthy values, deny access. This contract is identical for direct rules, `composeRules`, `anyRule`, and `createRule`, including async results. `composeRules`, `anyRule`, and `createRule` require function arguments. `anyRule` may continue after a denial or error to a different granting rule.
|
|
1964
|
+
|
|
1927
1965
|
### Rule Helpers
|
|
1928
1966
|
|
|
1929
1967
|
Simfinity.js provides reusable rule builders:
|
|
@@ -1968,6 +2006,8 @@ const permissions = {
|
|
|
1968
2006
|
|
|
1969
2007
|
Requires the user to have a specific role. Supports custom paths:
|
|
1970
2008
|
|
|
2009
|
+
The required role must be a nonempty string or nonempty array of nonempty strings; invalid configuration throws `TypeError`. The user's single role value must match an allowed string exactly.
|
|
2010
|
+
|
|
1971
2011
|
```javascript
|
|
1972
2012
|
const permissions = {
|
|
1973
2013
|
Query: {
|
|
@@ -1988,6 +2028,8 @@ const permissions = {
|
|
|
1988
2028
|
|
|
1989
2029
|
Requires the user to have specific permission(s). Supports custom paths:
|
|
1990
2030
|
|
|
2031
|
+
The required permission must be a nonempty string or nonempty array of nonempty strings; invalid configuration throws `TypeError`. The user's claim must be an array of nonempty strings. Entries match exactly; only a standalone `'*'` entry grants every permission. A claim such as `'posts:read'` must be supplied as `['posts:read']`. String claims, substrings, and embedded wildcard characters do not grant access.
|
|
2032
|
+
|
|
1991
2033
|
```javascript
|
|
1992
2034
|
const permissions = {
|
|
1993
2035
|
Mutation: {
|
|
@@ -2039,6 +2081,8 @@ const permissions = {
|
|
|
2039
2081
|
|
|
2040
2082
|
Checks if the authenticated user owns the resource:
|
|
2041
2083
|
|
|
2084
|
+
IDs must be nonempty strings, finite numbers (including zero), or Mongoose `ObjectId` instances. Numbers compare by their string representation, and ObjectIds compare by hexadecimal value. Missing/null IDs, empty strings, arrays, booleans, and arbitrary objects deny access. Use the path or extractor arguments to obtain a supported ID from a custom identity object.
|
|
2085
|
+
|
|
2042
2086
|
```javascript
|
|
2043
2087
|
const permissions = {
|
|
2044
2088
|
Post: {
|
|
@@ -2086,7 +2130,13 @@ Use `{ ref: 'path' }` to reference values:
|
|
|
2086
2130
|
|
|
2087
2131
|
**Security:**
|
|
2088
2132
|
- Only `parent`, `args`, and `ctx` roots are allowed
|
|
2089
|
-
-
|
|
2133
|
+
- Reference paths support root values, dotted fields, document getters, and numeric array indices. Empty path segments and `__proto__`, `prototype`, or `constructor` segments are invalid
|
|
2134
|
+
- `eq` and `in` require exactly two operands; `in` requires an array or a reference resolving to an array. `allOf` and `anyOf` require arrays of valid expressions; `not` requires a valid expression
|
|
2135
|
+
- Unknown operators or malformed nested expressions invalidate the entire configured policy, even inside an otherwise granting `anyOf`
|
|
2136
|
+
- Factories reject malformed expressions with `TypeError`; `isPolicyExpression` validates the complete AST, and direct `evaluateExpression` calls return `false` for malformed ASTs
|
|
2137
|
+
- Missing references and invalid runtime membership operands cannot grant access, including under `not` or repeated negation. They propagate through `allOf` and implicit AND. A separate valid `anyOf` branch can still grant access, such as a published post without an authenticated user
|
|
2138
|
+
- Explicit `null`, `false`, `0`, and empty-string comparison values remain valid; equality is strict. Use `requireAuth()` or `isOwner()` for identity checks rather than treating two explicit null IDs as ownership
|
|
2139
|
+
- Boolean expressions and logical identities remain valid: `{ allOf: [] }` is true, `{ anyOf: [] }` is false, and `{ not: false }` is true. Multiple operator keys form an implicit AND
|
|
2090
2140
|
- No `eval()` or `Function()` - pure object traversal
|
|
2091
2141
|
|
|
2092
2142
|
### Integration with GraphQL Yoga / Envelop
|
|
@@ -2246,6 +2296,8 @@ simfinity.use((params, next) => {
|
|
|
2246
2296
|
|
|
2247
2297
|
Each middleware receives a `params` object containing:
|
|
2248
2298
|
|
|
2299
|
+
Generated non-embedded relationship reads and nested collection mutations also invoke middleware for the related type. Middleware and scope callbacks are awaited. Nested child operations use the same argument shapes and request context as their root equivalents; review middleware that previously assumed it ran only once per root operation.
|
|
2300
|
+
|
|
2249
2301
|
```javascript
|
|
2250
2302
|
simfinity.use((params, next) => {
|
|
2251
2303
|
// params object contains:
|
|
@@ -2591,18 +2643,18 @@ Each generated tool follows MCP best practices so an agent can use it without ex
|
|
|
2591
2643
|
|
|
2592
2644
|
- **`description`**: reuses the GraphQL type/field descriptions and adds an actionable summary. List and aggregate tools spell out the available filter operators (`EQ, NE, LT, LTE, GT, GTE, IN, NIN, BTW, LIKE`), `AND`/`OR` groups, pagination and sorting; aggregate tools additionally document the `SUM/COUNT/AVG/MIN/MAX` operations and the `aggregation` argument.
|
|
2593
2645
|
- **`title`**: a human-readable label (e.g. `List Book`, `Create Book`, `Aggregate Book`).
|
|
2594
|
-
- **`inputSchema`**: JSON Schema derived
|
|
2595
|
-
- **`outputSchema`**: JSON Schema describing the returned data (mirroring the auto-generated selection set), with type/field descriptions. Every nullable GraphQL position also accepts `null` (e.g. `type: ['string', 'null']`; enums get `null` appended), so validating MCP clients accept the `null`s GraphQL legitimately returns in `structuredContent` (get-by-id misses, unset optional fields).
|
|
2646
|
+
- **`inputSchema`**: JSON Schema derived from the GraphQL arguments (scalars, enums, input objects, lists, non-null wrappers, and recursive filter types such as `QLFilterGroup` via `$defs`/`$ref`). Nullable arguments, input fields and list items accept explicit `null`; non-null positions reject it. Nullable references use `anyOf` with a null alternative. GraphQL type and field descriptions are propagated, with curated descriptions for synthetic types. `Date`/`DateTime`/`Time` scalars use the matching JSON Schema `format`. Defaulted arguments are optional; representable defaults appear in the schema and generated GraphQL variables, with enum member names rather than internal values. Omitted opaque scalar defaults that have no GraphQL literal representation use the field's original default.
|
|
2647
|
+
- **`outputSchema`**: JSON Schema describing the returned data (mirroring the auto-generated selection set), with type/field descriptions. Every nullable GraphQL position also accepts `null` (e.g. `type: ['string', 'null']`; enums get `null` appended), so validating MCP clients accept the `null`s GraphQL legitimately returns in `structuredContent` (get-by-id misses, unset optional fields).
|
|
2596
2648
|
- **`annotations`**: behavioral hints — queries are `readOnlyHint: true`, `delete*` is `destructiveHint: true`, `update*` is `idempotentHint: true`. Generated CRUD mutations are recognized by the placeholder descriptions Simfinity stamps on them, so a custom mutation that happens to be named `updateReport` (and carries its own description) keeps that description and gets no inferred hints.
|
|
2597
2649
|
|
|
2598
2650
|
On execution, each tool returns both a serialized JSON `text` content block and a machine-readable `structuredContent` (the GraphQL `data`) that conforms to the `outputSchema`. When the caller requests `pagination: { count: true }` on a **list** tool, the total record count is delivered in the tool result `_meta.count` (aggregate tools ignore the flag — their resolver never computes a total).
|
|
2599
2651
|
|
|
2600
|
-
> The MCP transports require the optional `@modelcontextprotocol/sdk` dependency (`^1.13.0` or newer). Install it with `npm install @modelcontextprotocol/sdk`. `generateMCPTools` works without it. SDK load problems raise distinct error codes: `MCP_SDK_NOT_INSTALLED` (not installed), `MCP_SDK_INCOMPATIBLE` (installed but too old to provide the requested transport — upgrade it), `MCP_SDK_LOAD_FAILED` (any other import failure).
|
|
2652
|
+
> The MCP transports require the optional `@modelcontextprotocol/sdk` dependency (`^1.13.0` or newer). Install it with `npm install @modelcontextprotocol/sdk@^1.13.0`. `generateMCPTools` works without it. SDK load problems raise distinct error codes: `MCP_SDK_NOT_INSTALLED` (not installed), `MCP_SDK_INCOMPATIBLE` (installed but too old to provide the requested transport — upgrade it), `MCP_SDK_LOAD_FAILED` (any other import failure).
|
|
2601
2653
|
|
|
2602
2654
|
### Installation
|
|
2603
2655
|
|
|
2604
2656
|
```bash
|
|
2605
|
-
npm install @modelcontextprotocol/sdk
|
|
2657
|
+
npm install @modelcontextprotocol/sdk@^1.13.0
|
|
2606
2658
|
```
|
|
2607
2659
|
|
|
2608
2660
|
### Generating tool definitions
|
|
@@ -2689,14 +2741,14 @@ The handler catches all errors itself: `onError` is for logging/metrics, and a J
|
|
|
2689
2741
|
| `include` / `exclude` | — | String or array: raw GraphQL field names, prefixed tool names, or the categories `'query'` / `'mutation'`. `exclude` wins over `include`. |
|
|
2690
2742
|
| `includeTypes` / `excludeTypes` | — | String or array of entity (return) type names — one entry covers every tool for that entity (`'Book'` matches `book`, `books`, `books_aggregate`, `addbook`, ...). `excludeTypes` wins. |
|
|
2691
2743
|
| `selectionDepth` | `1` | Nesting depth for the auto-generated output selection set (and the mirrored `outputSchema`). |
|
|
2692
|
-
| `includeId` | `true` |
|
|
2744
|
+
| `includeId` | `true` | Select a scalar or enum `id` as a fallback when nothing else is selectable; otherwise use `__typename`. |
|
|
2693
2745
|
| `toolNamePrefix` | — | Prefix prepended to every published tool name (e.g. `'catalog_'`). Must match `/^[a-zA-Z0-9_-]+$/`; the resulting names must match `/^[a-zA-Z0-9_-]{1,128}$/` (`MCP_INVALID_TOOL_NAME` otherwise). |
|
|
2694
2746
|
| `toolOverrides` | `{}` | Per-tool overrides keyed by tool (or unprefixed field) name: `{ description, title, annotations, selectionDepth, includeId, selection }`. See [Customizing tools](#customizing-tools). |
|
|
2695
2747
|
| `toolMiddleware` | — | Array of koa-style `async (call, next)` functions run around every tool call. See [Tool middleware](#tool-middleware). |
|
|
2696
2748
|
| `limits` | — | Guardrails: `{ maxPageSize, defaultPagination, maxResultBytes }`. See [Limits](#limits). |
|
|
2697
2749
|
| `schemaPlugins` | — | Envelop-style plugins whose `onSchemaChange` hook is applied once before serving (in-process execution only). See [Authentication](#authentication). |
|
|
2698
2750
|
| `serverName` / `serverVersion` | `'simfinity-mcp'` / `'1.0.0'` | Identity reported by the MCP server. |
|
|
2699
|
-
| `transportOptions` | — | `createHTTPMCPHandler` only:
|
|
2751
|
+
| `transportOptions` | — | `createHTTPMCPHandler` only: stateless SDK transport options (e.g. `enableDnsRebindingProtection`, `allowedHosts`, `allowedOrigins`). Setting `sessionIdGenerator`, `onsessioninitialized`, `onsessionclosed` or `eventStore` raises `MCP_INVALID_TRANSPORT_OPTIONS` at setup. |
|
|
2700
2752
|
| `onError` | — | `createHTTPMCPHandler` only: `(err, req, res)` callback invoked when a request fails; the handler still responds with a JSON-RPC internal error (HTTP 500) if headers were not sent. |
|
|
2701
2753
|
|
|
2702
2754
|
Selection sets always fall back to `id` and then `__typename` when nothing else is selectable (interface/union return types select `__typename`), so the generated documents are always statically valid — and the output schemas mirror the same fallbacks.
|
|
@@ -2762,7 +2814,7 @@ const { callTool } = simfinity.generateMCPTools(schema, {
|
|
|
2762
2814
|
|
|
2763
2815
|
- `maxPageSize`: rejects calls whose `pagination.size` exceeds the cap.
|
|
2764
2816
|
- `defaultPagination`: injected only for tools that have a `pagination` argument, and only when the caller did not send one. `page` and `size` are both required (QLPagination declares them non-null), and `size` must not exceed `maxPageSize` — that misconfiguration throws `MCP_INVALID_LIMITS` at setup.
|
|
2765
|
-
- `maxResultBytes`:
|
|
2817
|
+
- `maxResultBytes`: caps the UTF-8 byte length of the pretty-printed logical payload: `data` on success, or `{ errors, data? }` on failure. Oversized errors and partial data are replaced by `MCP_RESULT_TOO_LARGE`. This diagnostic and pagination-limit diagnostics are exempt from the cap, so even a tiny cap can return an actionable error. The cap excludes MCP envelope overhead, the duplicate `structuredContent`, and middleware-created results; it runs after execution and does not limit database work or remote download size.
|
|
2766
2818
|
|
|
2767
2819
|
### Tool middleware
|
|
2768
2820
|
|
|
@@ -2809,9 +2861,9 @@ const { callTool } = simfinity.generateMCPTools(schema, {
|
|
|
2809
2861
|
});
|
|
2810
2862
|
```
|
|
2811
2863
|
|
|
2812
|
-
- `timeoutMs` aborts slow
|
|
2864
|
+
- `timeoutMs` aborts slow attempts, including reading the response body (`AbortSignal.timeout`); the timeout signal is combined with the MCP client's cancellation signal. Client cancellation rejects during either fetch or body reading and is never retried.
|
|
2813
2865
|
- `retry` re-issues failed **queries only — never mutations** — after network errors/timeouts, HTTP 5xx and 429 responses. Backoff is linear: the first retry waits `backoffMs`, the second `2 × backoffMs`, and so on (default `backoffMs`: 250).
|
|
2814
|
-
- Transport failures
|
|
2866
|
+
- Transport failures surface as `isError` tool results carrying GraphQL-shaped errors with the codes `MCP_REMOTE_HTTP_ERROR` (non-2xx response without a valid GraphQL body, includes `extensions.status`), `MCP_REMOTE_REQUEST_FAILED` (network error/timeout, including body-read failures) and `MCP_REMOTE_INVALID_RESPONSE` (non-JSON body or invalid GraphQL response shape). A valid body has object `data` or a nonempty `errors` array with string messages; `data: null` requires errors. Invalid successful HTTP responses are not retried. Client cancellation rejects instead of returning an error tool result.
|
|
2815
2867
|
- If the remote response carries a numeric `extensions.count` (e.g. exposed via `simfinity.plugins.envelopCountPlugin()`), it is surfaced as `_meta.count` on the tool result.
|
|
2816
2868
|
|
|
2817
2869
|
`schemaPlugins` is not applied in remote mode — the remote GraphQL server enforces its own plugins.
|
|
@@ -2821,9 +2873,9 @@ const { callTool } = simfinity.generateMCPTools(schema, {
|
|
|
2821
2873
|
Successful calls return `{ content: [textJSON], structuredContent: data, isError: false }`. Failures and edge cases behave as follows:
|
|
2822
2874
|
|
|
2823
2875
|
- **Total count**: with `pagination: { count: true }` on a **list** tool, the count lands in `_meta.count` on the tool result — in-process it is read back from a per-call context layer (Simfinity's find resolver writes it there; counts never leak across calls), in remote mode from the response `extensions.count`. Aggregate tools never deliver a count.
|
|
2824
|
-
- **Execution errors**: `isError: true` with a `text` payload of `{ errors, data? }` — partial data is preserved alongside
|
|
2876
|
+
- **Execution errors**: `isError: true` with a `text` payload of `{ errors, data? }` — partial data is preserved alongside errors when it fits `maxResultBytes`. `structuredContent` is omitted on errors.
|
|
2825
2877
|
- **Remote transport failures** and **limit violations** are also `isError` results (see [Remote execution](#remote-execution) and [Limits](#limits) for the codes).
|
|
2826
|
-
- **Unknown tool names** are the exception: `callTool` and `getOperation` throw `MCP_TOOL_NOT_FOUND`.
|
|
2878
|
+
- **Unknown tool names** are the exception: `callTool` and `getOperation` throw `MCP_TOOL_NOT_FOUND`. An aborted `extra.signal` throws `MCP_CALL_CANCELLED` before execution, including cancellation while awaiting an asynchronous context factory. This prevents starting the GraphQL operation after cancellation; it does not undo or cancel database work already started.
|
|
2827
2879
|
|
|
2828
2880
|
### Authentication
|
|
2829
2881
|
|
|
@@ -3056,9 +3108,10 @@ The `groupId` and `path` parameters support:
|
|
|
3056
3108
|
|
|
3057
3109
|
### Pagination Notes
|
|
3058
3110
|
|
|
3059
|
-
-
|
|
3111
|
+
- Explicit `page` and `size` must be positive safe integers, and `size` must fit the configured maximum (1000 by default). Invalid input produces `INVALID_PAGINATION` with status 400.
|
|
3060
3112
|
- The `count` parameter is **ignored** for aggregation queries
|
|
3061
3113
|
- Pagination is applied **after** grouping and sorting
|
|
3114
|
+
- Aggregation queries remain unbounded when pagination is omitted; they do not inherit the list default limit.
|
|
3062
3115
|
|
|
3063
3116
|
### MongoDB Translation
|
|
3064
3117
|
|
|
@@ -3501,6 +3554,16 @@ query {
|
|
|
3501
3554
|
|
|
3502
3555
|
Simfinity.js supports built-in pagination with optional total count:
|
|
3503
3556
|
|
|
3557
|
+
Configure the process-wide maximum once during application startup:
|
|
3558
|
+
|
|
3559
|
+
```javascript
|
|
3560
|
+
import * as simfinity from '@simtlix/simfinity-js';
|
|
3561
|
+
|
|
3562
|
+
simfinity.configureQueryLimits({ maxPageSize: 500 });
|
|
3563
|
+
```
|
|
3564
|
+
|
|
3565
|
+
The default maximum is **1000**. Explicit `page` and `size` must be positive safe integers, `size` must not exceed the maximum, and the computed skip must remain a safe integer. Invalid pagination throws `INVALID_PAGINATION` (400); invalid configuration throws `INVALID_QUERY_LIMITS` (400). With no pagination, a list returns at most `Math.min(100, maxPageSize)` records. Any positive safe-integer maximum is allowed, including values below 100. Call `configureQueryLimits()` to restore the default maximum. This deliberately tightens previously unbounded explicit page sizes; applications needing larger pages can configure an appropriate maximum. Unpaginated aggregate queries retain their existing behavior.
|
|
3566
|
+
|
|
3504
3567
|
```graphql
|
|
3505
3568
|
query {
|
|
3506
3569
|
series(
|
|
@@ -3588,6 +3651,8 @@ See the [Plugins for Count in Extensions](#-plugins-for-count-in-extensions) sec
|
|
|
3588
3651
|
|
|
3589
3652
|
### 11. Sorting
|
|
3590
3653
|
|
|
3654
|
+
List sort fields must resolve to declared scalar or enum paths, and every term requires `ASC` or `DESC`. Root `id` and related `author.id` sort by their stored `_id` paths. Embedded paths remain dotted, and repeated sort terms through one relationship reuse the same lookup. Supply at least one term; invalid sort input is rejected before executing the list query.
|
|
3655
|
+
|
|
3591
3656
|
Simfinity.js supports sorting with multiple fields and sort orders:
|
|
3592
3657
|
|
|
3593
3658
|
```graphql
|
|
@@ -4205,17 +4270,21 @@ console.log(BookInput.getFields()); // Input fields for mutations
|
|
|
4205
4270
|
|
|
4206
4271
|
### `saveObject(typeName, args, session?, context?)`
|
|
4207
4272
|
|
|
4208
|
-
Programmatically save an object
|
|
4273
|
+
Programmatically save an object through the creation pipeline, including validation, controllers, and nested writes.
|
|
4209
4274
|
|
|
4210
4275
|
**Parameters:**
|
|
4211
4276
|
- `typeName` (string): The name of the GraphQL type
|
|
4212
4277
|
- `args` (object): The data to save
|
|
4213
|
-
- `session` (MongooseSession, optional):
|
|
4278
|
+
- `session` (MongooseSession, optional): Caller-owned session with an active transaction, from the same MongoDB client as the registered model
|
|
4214
4279
|
- `context` (object, optional): GraphQL context object (includes request info, user data, etc.)
|
|
4215
4280
|
|
|
4216
4281
|
**Returns:**
|
|
4217
4282
|
- `Promise<object>`: The saved object
|
|
4218
4283
|
|
|
4284
|
+
Without a session, `saveObject()` starts a transaction on the registered model's connection, commits the parent and nested writes together, and awaits session cleanup. The same bounded transaction and commit retries described above apply. This requires a transaction-capable MongoDB deployment, such as a replica set.
|
|
4285
|
+
|
|
4286
|
+
With a session, it participates in the caller's active transaction. It never starts, commits, aborts, retries, or ends that caller-owned transaction/session; the caller must handle failures and finish the transaction. An inactive supplied session is rejected with `ACTIVE_TRANSACTION_REQUIRED` (400) before writes. Pass the provided session when calling from a controller or custom mutation so the writes share its transaction. Direct calls bypass GraphQL input coercion, field authorization, and global middleware.
|
|
4287
|
+
|
|
4219
4288
|
**Example:**
|
|
4220
4289
|
|
|
4221
4290
|
```javascript
|
|
@@ -4252,5 +4321,3 @@ const schema = simfinity.createSchema();
|
|
|
4252
4321
|
```
|
|
4253
4322
|
|
|
4254
4323
|
*Built with ❤️ by [Simtlix](https://github.com/simtlix)*
|
|
4255
|
-
|
|
4256
|
-
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simtlix/simfinity-js",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "./types/index.d.ts",
|
|
@@ -9,8 +9,14 @@
|
|
|
9
9
|
"test": "vitest run",
|
|
10
10
|
"test:watch": "vitest",
|
|
11
11
|
"test:coverage": "vitest --coverage",
|
|
12
|
-
"lint": "eslint
|
|
13
|
-
"lint-fix": "eslint --fix
|
|
12
|
+
"lint": "eslint \"**/*.js\"",
|
|
13
|
+
"lint-fix": "eslint --fix \"**/*.js\"",
|
|
14
|
+
"test:integration": "vitest run tests/integration",
|
|
15
|
+
"test:packages": "node scripts/test-packages.js",
|
|
16
|
+
"docs:install": "npm ci --prefix docs",
|
|
17
|
+
"docs:dev": "npm run dev --prefix docs --",
|
|
18
|
+
"docs:build": "npm run build --prefix docs",
|
|
19
|
+
"docs:preview": "npm run preview --prefix docs --"
|
|
14
20
|
},
|
|
15
21
|
"engines": {
|
|
16
22
|
"node": ">=18.18.0"
|
|
@@ -19,7 +25,7 @@
|
|
|
19
25
|
"license": "Apache-2.0",
|
|
20
26
|
"repository": {
|
|
21
27
|
"type": "git",
|
|
22
|
-
"url": "
|
|
28
|
+
"url": "https://github.com/simtlix/simfinity.js"
|
|
23
29
|
},
|
|
24
30
|
"peerDependencies": {
|
|
25
31
|
"graphql": "^16.11.0",
|
|
@@ -39,5 +45,12 @@
|
|
|
39
45
|
"graphql": "^16.11.0",
|
|
40
46
|
"graphql-middleware": "^6.1.35",
|
|
41
47
|
"mongoose": "^8.16.2"
|
|
48
|
+
},
|
|
49
|
+
"workspaces": [
|
|
50
|
+
"packages/*"
|
|
51
|
+
],
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@simtlix/simfinity-core": "3.2.0",
|
|
54
|
+
"@simtlix/simfinity-mcp": "3.2.0"
|
|
42
55
|
}
|
|
43
56
|
}
|
package/src/auth/errors.js
CHANGED
|
@@ -1,44 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Authentication error - thrown when user is not authenticated
|
|
5
|
-
* Uses code: UNAUTHENTICATED, status: 401
|
|
6
|
-
*/
|
|
7
|
-
export class UnauthenticatedError extends SimfinityError {
|
|
8
|
-
constructor(message = 'Authentication required') {
|
|
9
|
-
super(message, 'UNAUTHENTICATED', 401);
|
|
10
|
-
this.name = 'UnauthenticatedError';
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Authorization error - thrown when user lacks permission
|
|
16
|
-
* Uses code: FORBIDDEN, status: 403
|
|
17
|
-
*/
|
|
18
|
-
export class ForbiddenError extends SimfinityError {
|
|
19
|
-
constructor(message = 'Access denied') {
|
|
20
|
-
super(message, 'FORBIDDEN', 403);
|
|
21
|
-
this.name = 'ForbiddenError';
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Generic auth error factory for custom auth failures
|
|
27
|
-
* @param {string} message - Error message
|
|
28
|
-
* @param {string} code - Error code (UNAUTHENTICATED or FORBIDDEN)
|
|
29
|
-
* @returns {SimfinityError}
|
|
30
|
-
*/
|
|
31
|
-
export const createAuthError = (message, code = 'FORBIDDEN') => {
|
|
32
|
-
const status = code === 'UNAUTHENTICATED' ? 401 : 403;
|
|
33
|
-
return new SimfinityError(message, code, status);
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
// Export all errors as an object for convenience
|
|
37
|
-
const errors = {
|
|
38
|
-
UnauthenticatedError,
|
|
39
|
-
ForbiddenError,
|
|
40
|
-
createAuthError,
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
export default errors;
|
|
44
|
-
|
|
1
|
+
export * from '@simtlix/simfinity-core/auth/errors';
|
|
2
|
+
export { default } from '@simtlix/simfinity-core/auth/errors';
|