@simtlix/simfinity-core 3.5.6 → 3.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/package.json +1 -1
- package/src/runtime.js +41 -3
- package/src/scalars/factory.js +6 -0
package/package.json
CHANGED
package/src/runtime.js
CHANGED
|
@@ -24,6 +24,26 @@ import './introspection.js';
|
|
|
24
24
|
// created is application code, such as a masking resolver, recorded once per type so later
|
|
25
25
|
// in-place wrapping (for example by the auth plugin) does not change the snapshot.
|
|
26
26
|
const generatedResolvers = new WeakSet();
|
|
27
|
+
// A caller that runs one operation per call, such as simfinity-mcp, can collect the pagination
|
|
28
|
+
// count through the root value instead of the shared context: a function stored under this key
|
|
29
|
+
// receives the count. The key is not exported; simfinity-mcp defines the same Symbol.for key.
|
|
30
|
+
const COUNT_SINK = Symbol.for('simfinity.countSink');
|
|
31
|
+
const countSinkOf = (value) => {
|
|
32
|
+
if (value === null || (typeof value !== 'object' && typeof value !== 'function')) return undefined;
|
|
33
|
+
const sink = value[COUNT_SINK];
|
|
34
|
+
return typeof sink === 'function' ? sink : undefined;
|
|
35
|
+
};
|
|
36
|
+
// The root value arrives as parent, which resolver wrappers that forward only (parent, args,
|
|
37
|
+
// context) still pass on; info.rootValue is the fallback. Without a sink the count goes to
|
|
38
|
+
// context.count, where Envelop/Apollo/Yoga count plugins read it.
|
|
39
|
+
const reportCount = (parent, info, context, count) => {
|
|
40
|
+
const sink = countSinkOf(parent) || (info ? countSinkOf(info.rootValue) : undefined);
|
|
41
|
+
if (sink) {
|
|
42
|
+
sink(count);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
context.count = count;
|
|
46
|
+
};
|
|
27
47
|
const applicationResolvedFields = new WeakMap();
|
|
28
48
|
const markGenerated = (resolve) => {
|
|
29
49
|
generatedResolvers.add(resolve);
|
|
@@ -1508,6 +1528,9 @@ const resolveById = async (type, args, context, requiredId) => {
|
|
|
1508
1528
|
return results[0] || null;
|
|
1509
1529
|
};
|
|
1510
1530
|
|
|
1531
|
+
// Every generated mutation field carries extensions.simfinityMutation with its operation (and, for
|
|
1532
|
+
// a state-machine action, the action and its from/to states), so tools such as the MCP package can
|
|
1533
|
+
// classify it without guessing from its name. Extensions do not appear in SDL or introspection.
|
|
1511
1534
|
const buildMutation = (name, includedMutationTypes, includedCustomMutations) => {
|
|
1512
1535
|
const rootQueryArgs = {};
|
|
1513
1536
|
rootQueryArgs.name = name;
|
|
@@ -1524,6 +1547,7 @@ const buildMutation = (name, includedMutationTypes, includedCustomMutations) =>
|
|
|
1524
1547
|
type: type.gqltype,
|
|
1525
1548
|
description: 'add',
|
|
1526
1549
|
args: argsObject,
|
|
1550
|
+
extensions: { simfinityMutation: { typeName: type.gqltype.name, operation: operations.SAVE } },
|
|
1527
1551
|
async resolve(parent, args, context) {
|
|
1528
1552
|
const params = {
|
|
1529
1553
|
type,
|
|
@@ -1541,6 +1565,7 @@ const buildMutation = (name, includedMutationTypes, includedCustomMutations) =>
|
|
|
1541
1565
|
type: type.gqltype,
|
|
1542
1566
|
description: 'delete',
|
|
1543
1567
|
args: { id: { type: new GraphQLNonNull(GraphQLID) } },
|
|
1568
|
+
extensions: { simfinityMutation: { typeName: type.gqltype.name, operation: operations.DELETE } },
|
|
1544
1569
|
async resolve(parent, args, context) {
|
|
1545
1570
|
const params = {
|
|
1546
1571
|
type,
|
|
@@ -1566,6 +1591,7 @@ const buildMutation = (name, includedMutationTypes, includedCustomMutations) =>
|
|
|
1566
1591
|
type: type.gqltype,
|
|
1567
1592
|
description: 'update',
|
|
1568
1593
|
args: argsObject,
|
|
1594
|
+
extensions: { simfinityMutation: { typeName: type.gqltype.name, operation: operations.UPDATE } },
|
|
1569
1595
|
async resolve(parent, args, context) {
|
|
1570
1596
|
const params = {
|
|
1571
1597
|
type,
|
|
@@ -1586,6 +1612,15 @@ const buildMutation = (name, includedMutationTypes, includedCustomMutations) =>
|
|
|
1586
1612
|
type: type.gqltype,
|
|
1587
1613
|
description: actionField.description,
|
|
1588
1614
|
args: argsObject,
|
|
1615
|
+
extensions: {
|
|
1616
|
+
simfinityMutation: {
|
|
1617
|
+
typeName: type.gqltype.name,
|
|
1618
|
+
operation: operations.STATE_CHANGED,
|
|
1619
|
+
action: actionName,
|
|
1620
|
+
from: actionField.from && actionField.from.name,
|
|
1621
|
+
to: actionField.to && actionField.to.name,
|
|
1622
|
+
},
|
|
1623
|
+
},
|
|
1589
1624
|
async resolve(parent, args, context) {
|
|
1590
1625
|
const params = {
|
|
1591
1626
|
type,
|
|
@@ -1617,6 +1652,7 @@ const buildMutation = (name, includedMutationTypes, includedCustomMutations) =>
|
|
|
1617
1652
|
type: registeredMutation.outputModel,
|
|
1618
1653
|
description: registeredMutation.description,
|
|
1619
1654
|
args: argsObject,
|
|
1655
|
+
extensions: { simfinityMutation: { operation: operations.CUSTOM_MUTATION } },
|
|
1620
1656
|
async resolve(parent, args, context) {
|
|
1621
1657
|
const params = {
|
|
1622
1658
|
args,
|
|
@@ -1657,8 +1693,10 @@ const buildRootQuery = (name, includedTypes) => {
|
|
|
1657
1693
|
rootQueryArgs.fields[type.listEntitiesEndpointName] = {
|
|
1658
1694
|
type: new GraphQLList(type.gqltype),
|
|
1659
1695
|
args: argsObject,
|
|
1660
|
-
|
|
1661
|
-
|
|
1696
|
+
// countSink: this resolver reports pagination counts through reportCount, so a caller
|
|
1697
|
+
// that supplies a root-value sink can pass its context unchanged.
|
|
1698
|
+
extensions: { simfinityQuery: { typeName: type.gqltype.name, operation: 'find', countSink: true } },
|
|
1699
|
+
async resolve(parent, args, context, info) {
|
|
1662
1700
|
const joinedScopes = inspectClientPaths(type.gqltype, args, 'find');
|
|
1663
1701
|
const params = {
|
|
1664
1702
|
type,
|
|
@@ -1679,7 +1717,7 @@ const buildRootQuery = (name, includedTypes) => {
|
|
|
1679
1717
|
|
|
1680
1718
|
const [result, resultCount] = await Promise.all([dataPromise, countPromise]);
|
|
1681
1719
|
if (wantsCount) {
|
|
1682
|
-
context
|
|
1720
|
+
reportCount(parent, info, context, resultCount);
|
|
1683
1721
|
}
|
|
1684
1722
|
return result;
|
|
1685
1723
|
},
|
package/src/scalars/factory.js
CHANGED
|
@@ -3,6 +3,11 @@ import { GraphQLScalarType, GraphQLString, GraphQLInt, GraphQLFloat, GraphQLBool
|
|
|
3
3
|
// The base each validated scalar delegates parsing to. A base exists before its scalar, so these links cannot cycle.
|
|
4
4
|
const validatedBases = new WeakMap();
|
|
5
5
|
|
|
6
|
+
// The same link as a non-enumerable property, so MCP (packages/mcp/src/index.js, VALIDATED_SCALAR_BASE) can tell a
|
|
7
|
+
// validated scalar, whose output is its base's, from a hand-set `baseScalarType` storage hint, whose own `serialize`
|
|
8
|
+
// decides the output. Not exported; Symbol.for gives both packages the same key.
|
|
9
|
+
const VALIDATED_SCALAR_BASE = Symbol.for('simfinity.validatedScalarBase');
|
|
10
|
+
|
|
6
11
|
// Follow validated-scalar chains to the scalar that defines the literal syntax. Any other scalar is the root, even
|
|
7
12
|
// with a hand-set `baseScalarType` storage hint, because its own parseLiteral decides which literals it takes.
|
|
8
13
|
const findRootScalar = (scalarType) => {
|
|
@@ -68,5 +73,6 @@ export function createValidatedScalar(name, description, baseScalarType, validat
|
|
|
68
73
|
|
|
69
74
|
scalar.baseScalarType = baseScalarType;
|
|
70
75
|
validatedBases.set(scalar, baseScalarType);
|
|
76
|
+
Object.defineProperty(scalar, VALIDATED_SCALAR_BASE, { value: baseScalarType });
|
|
71
77
|
return scalar;
|
|
72
78
|
}
|