@simtlix/simfinity-core 3.5.7 → 3.5.8

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
@@ -16,7 +16,7 @@ For relational storage, `@simtlix/simfinity-sql` builds on core and delegates ph
16
16
  - `preventCreatingCollection` to pass validation-only storage setup to an adapter;
17
17
  - `configureQueryLimits` and `configureMutationLimits` for the process-wide page-size and nested-operation limits.
18
18
 
19
- The adapter supplies model creation, identifiers, transactions, record reads and writes, queries, counts, aggregations, and inverse collection reads. `DatabaseAdapter`, `Runtime`, controller, middleware, registration, and state-machine interfaces are exported in the TypeScript declarations. A runtime binds one adapter; create another runtime to use another database or configuration. An adapter can also define `getByIds(model, ids, { context })`, which returns the records it finds in `getById` shape and any order; the runtime then batches generated single-reference reads per request and type, matching records by `castId`, splitting batches at `maxPageSize`, and reading an ID with `getById` when a batch read fails or when `castId` would normalize or reject it. `castId` should throw a `NOT_VALID_ID` (400) `SimfinityError` for a malformed value and must not create a new identifier. When `getById` rejects a stored reference value that way, including one that `castId` rejected first, the generated reference field fails with an `InternalServerError`, since the client did not send the value. An adapter that hydrates records can define `readEmbeddedValue(value)`: every nullable, singular embedded object field without a resolver then passes its value through it, so an explicitly stored `null` reads as `null`. The field is read as graphql's default resolver reads it, so a method on the parent is called and a promise it returns is awaited before the hook receives the value. The MongoDB adapter defines it.
19
+ The adapter supplies model creation, identifiers, transactions, record reads and writes, queries, counts, aggregations, and inverse collection reads. `DatabaseAdapter`, `Runtime`, controller, middleware, registration, and state-machine interfaces are exported in the TypeScript declarations. A runtime binds one adapter; create another runtime to use another database or configuration. An adapter can also define `getByIds(model, ids, { context })`, which returns the records it finds in `getById` shape and any order; the runtime then batches generated single-reference reads per request and type, matching records by `castId`, splitting batches at `maxPageSize`, and reading an ID with `getById` when a batch read fails or when `castId` would normalize or reject it. `castId` should throw a `NOT_VALID_ID` (400) `SimfinityError` for a malformed value and must not create a new identifier. When `getById` rejects a stored reference value that way, including one that `castId` rejected first, the generated reference field fails with an `InternalServerError` (500), since the client did not send the value. `update()` resolves to the updated record as stored, or null; nested collection children of that record are linked to and checked against its `_id`, else `id`, else the requested ID. An adapter that hydrates records can define `readEmbeddedValue(value)`: every nullable, singular embedded object field without a resolver then passes its value through it, so an explicitly stored `null` reads as `null`. The field is read as graphql's default resolver reads it, so a method on the parent is called and a promise it returns is awaited before the hook receives the value. The MongoDB adapter defines it.
20
20
 
21
21
  Entity types, those registered with `connect()` and the targets of non-embedded relations, resolve `id` to `_id`, falling back to `id`, whatever the `createSchema()` allowlists include; types used only as embedded values keep their declared `id`. Embedded copies of an entity type resolve the same way, so they return their declared `id` unless the adapter stores an `_id` for them, as MongoDB does for embedded list items. The resolver is set on the type object, so it applies in every runtime that shares the type. `createSchema()` marks these registrations with `storedIdentity: true` before calling `adapter.prepare()`.
22
22
 
@@ -35,3 +35,5 @@ Importing the runtime installs Simfinity's `__Field.extensions` introspection fi
35
35
  `createQueryPlan` and `resolveModelPath` turn Simfinity filter, sort, pagination, and aggregation inputs into a driver-neutral plan resolved against that metadata.
36
36
 
37
37
  The package also exports the `auth`, `validators`, `scalars`, and `plugins` helper namespaces alongside `createValidatedScalar`, `SimfinityError`, `InternalServerError`, `buildErrorFormatter`, `configureQueryLimits`, `configureMutationLimits`, `QLOperator`, `QLSort`, and `QLValue`. `buildErrorFormatter` returns a `GraphQLError` whose `originalError` is the classified Simfinity error. Database packages reuse these exports so helpers, errors, and globally named GraphQL types retain one identity.
38
+
39
+ The same helpers are available from the `@simtlix/simfinity-core/auth`, `/auth/errors`, `/auth/expressions`, `/auth/rules`, `/plugins`, `/scalars` and `/validators` subpaths, each with TypeScript declarations whose types match the root namespaces. `moduleResolution` `node16`, `nodenext` and `bundler` read them through the export map, and `node10` through `typesVersions`. The `internal/*` subpaths are not public API and have no declarations.
package/package.json CHANGED
@@ -1,24 +1,70 @@
1
1
  {
2
2
  "name": "@simtlix/simfinity-core",
3
- "version": "3.5.7",
3
+ "version": "3.5.8",
4
4
  "description": "Driver-free GraphQL runtime and model metadata for Simfinity",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
7
7
  "types": "./types/index.d.ts",
8
+ "typesVersions": {
9
+ "*": {
10
+ "auth": [
11
+ "types/auth/index.d.ts"
12
+ ],
13
+ "auth/errors": [
14
+ "types/auth/errors.d.ts"
15
+ ],
16
+ "auth/expressions": [
17
+ "types/auth/expressions.d.ts"
18
+ ],
19
+ "auth/rules": [
20
+ "types/auth/rules.d.ts"
21
+ ],
22
+ "plugins": [
23
+ "types/plugins.d.ts"
24
+ ],
25
+ "scalars": [
26
+ "types/scalars.d.ts"
27
+ ],
28
+ "validators": [
29
+ "types/validators.d.ts"
30
+ ]
31
+ }
32
+ },
8
33
  "exports": {
9
34
  ".": {
10
35
  "types": "./types/index.d.ts",
11
36
  "default": "./src/index.js"
12
37
  },
13
- "./auth": "./src/auth/index.js",
14
- "./auth/errors": "./src/auth/errors.js",
15
- "./auth/expressions": "./src/auth/expressions.js",
16
- "./auth/rules": "./src/auth/rules.js",
38
+ "./auth": {
39
+ "types": "./types/auth/index.d.ts",
40
+ "default": "./src/auth/index.js"
41
+ },
42
+ "./auth/errors": {
43
+ "types": "./types/auth/errors.d.ts",
44
+ "default": "./src/auth/errors.js"
45
+ },
46
+ "./auth/expressions": {
47
+ "types": "./types/auth/expressions.d.ts",
48
+ "default": "./src/auth/expressions.js"
49
+ },
50
+ "./auth/rules": {
51
+ "types": "./types/auth/rules.d.ts",
52
+ "default": "./src/auth/rules.js"
53
+ },
17
54
  "./internal/object-id": "./src/auth/object-id.js",
18
55
  "./internal/relation-storage": "./src/relation-storage.js",
19
- "./plugins": "./src/plugins.js",
20
- "./scalars": "./src/scalars.js",
21
- "./validators": "./src/validators.js"
56
+ "./plugins": {
57
+ "types": "./types/plugins.d.ts",
58
+ "default": "./src/plugins.js"
59
+ },
60
+ "./scalars": {
61
+ "types": "./types/scalars.d.ts",
62
+ "default": "./src/scalars.js"
63
+ },
64
+ "./validators": {
65
+ "types": "./types/validators.d.ts",
66
+ "default": "./src/validators.js"
67
+ }
22
68
  },
23
69
  "files": [
24
70
  "src",
@@ -3,19 +3,27 @@ import SimfinityError from '../errors/simfinity.error.js';
3
3
 
4
4
  const parseQLValue = (value) => value;
5
5
 
6
- const parseLiteral = (ast) => {
6
+ // graphql-js resolves a variable that is the whole value itself, but passes a list literal such as
7
+ // [$from, $to] here with the operation's variables (undefined during validation). A variable item
8
+ // the operation leaves unset reads as null, as graphql-js reads list items of other types. An enum
9
+ // literal reads as its name, like the quoted name of an enum filter value.
10
+ const parseLiteral = (ast, variables) => {
7
11
  switch (ast.kind) {
8
12
  case Kind.INT: return parseInt(ast.value, 10);
9
13
  case Kind.FLOAT: return parseFloat(ast.value);
10
14
  case Kind.BOOLEAN: return ast.value === true || ast.value === 'true';
11
- case Kind.STRING: return ast.value;
15
+ case Kind.STRING:
16
+ case Kind.ENUM: return ast.value;
12
17
  case Kind.NULL: return null;
18
+ case Kind.VARIABLE:
19
+ // Object.hasOwn: a variable named like an Object.prototype member is not inherited.
20
+ return variables != null && Object.hasOwn(variables, ast.name.value) ? variables[ast.name.value] : null;
13
21
  case Kind.LIST:
14
22
  return ast.values.map((item) => {
15
23
  if (item.kind === Kind.LIST) {
16
24
  throw new SimfinityError('Nested filter value lists are not supported', 'INVALID_FILTER_VALUE', 400);
17
25
  }
18
- return parseLiteral(item);
26
+ return parseLiteral(item, variables);
19
27
  });
20
28
  default:
21
29
  throw new SimfinityError('Filter values must be scalars or flat scalar lists', 'INVALID_FILTER_VALUE', 400);
@@ -1,8 +1,12 @@
1
1
  import SimfinityError from './simfinity.error.js';
2
2
 
3
+ // An unexpected failure: code INTERNAL_SERVER_ERROR and status 500. `cause` keeps the original error.
4
+ // The cause is never included in GraphQL responses, or in the errors that buildErrorFormatter returns
5
+ // when they are serialized. It is an enumerable own property of this error, so JSON.stringify(error),
6
+ // or a logger that copies enumerable properties, includes it.
3
7
  class InternalServerError extends SimfinityError {
4
8
  constructor(message, cause) {
5
- super(message, 'INTERNAL_SERVER_ERROR');
9
+ super(message, 'INTERNAL_SERVER_ERROR', 500);
6
10
  this.cause = cause;
7
11
  this.getCause = () => this.cause;
8
12
  }