@simtlix/simfinity-core 3.5.5 → 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 +113 -20
- package/src/scalars/factory.js +6 -0
- package/types/index.d.ts +5 -2
package/package.json
CHANGED
package/src/runtime.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
GraphQLObjectType, GraphQLString, GraphQLID, GraphQLSchema, GraphQLList,
|
|
5
5
|
GraphQLNonNull, GraphQLInputObjectType, GraphQLScalarType,
|
|
6
6
|
GraphQLInt, GraphQLEnumType, GraphQLBoolean, GraphQLError, GraphQLInterfaceType, GraphQLUnionType,
|
|
7
|
-
Kind,
|
|
7
|
+
Kind, getNamedType,
|
|
8
8
|
} from 'graphql';
|
|
9
9
|
|
|
10
10
|
import SimfinityError from './errors/simfinity.error.js';
|
|
@@ -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);
|
|
@@ -264,6 +284,29 @@ export const buildErrorFormatter = (callback) => (error) => {
|
|
|
264
284
|
|
|
265
285
|
export { SimfinityError, InternalServerError };
|
|
266
286
|
|
|
287
|
+
// A field named like an Object.prototype member (`constructor`, `toString`, ...) that reads as that
|
|
288
|
+
// very member holds no value: graphql input objects and stored records without the field inherit
|
|
289
|
+
// it, and MongoDB nested paths return it from their own accessors. Other values, overriding members
|
|
290
|
+
// such as class methods and getters, and every other name read as usual. The property is read once,
|
|
291
|
+
// so a getter runs once, as with a plain read.
|
|
292
|
+
const isObjectMember = (key, read) => key in Object.prototype && read === Object.prototype[key];
|
|
293
|
+
const ownValue = (value, key) => {
|
|
294
|
+
if (value == null) return undefined;
|
|
295
|
+
const read = value[key];
|
|
296
|
+
return isObjectMember(key, read) ? undefined : read;
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
// Output fields with such names read through this resolver instead of graphql's default one, which
|
|
300
|
+
// would call the inherited member. Otherwise it resolves as the default one does, calling a method
|
|
301
|
+
// with the parent as receiver, but reads the property only once. It reads no data, so installing it
|
|
302
|
+
// binds no type to a runtime.
|
|
303
|
+
const readOwnField = markGenerated((parent, args, context, info) => {
|
|
304
|
+
if (parent === null || (typeof parent !== 'object' && typeof parent !== 'function')) return undefined;
|
|
305
|
+
const property = parent[info.fieldName];
|
|
306
|
+
if (isObjectMember(info.fieldName, property)) return null;
|
|
307
|
+
return typeof property === 'function' ? property.call(parent, args, context, info) : property;
|
|
308
|
+
});
|
|
309
|
+
|
|
267
310
|
const cloneInput = (value, type) => {
|
|
268
311
|
if (value === null || value === undefined || !type) return value;
|
|
269
312
|
if (type instanceof GraphQLNonNull) return cloneInput(value, type.ofType);
|
|
@@ -791,28 +834,29 @@ const materializeModel = async (args, gqltype, linkToParent, operation, session)
|
|
|
791
834
|
const collectionFields = {};
|
|
792
835
|
|
|
793
836
|
for (const [fieldEntryName, fieldEntry] of Object.entries(argTypes)) {
|
|
837
|
+
const fieldValue = ownValue(args, fieldEntryName);
|
|
794
838
|
if (fieldEntry.extensions && fieldEntry.extensions.validations
|
|
795
839
|
&& fieldEntry.extensions.validations[operation]) {
|
|
796
840
|
for (const validator of fieldEntry.extensions.validations[operation]) {
|
|
797
|
-
await validator.validate(gqltype.name, fieldEntryName,
|
|
841
|
+
await validator.validate(gqltype.name, fieldEntryName, fieldValue, session);
|
|
798
842
|
}
|
|
799
843
|
}
|
|
800
844
|
|
|
801
|
-
if (
|
|
845
|
+
if (fieldValue !== undefined && fieldValue !== null) {
|
|
802
846
|
if (fieldEntry.type instanceof GraphQLScalarType
|
|
803
847
|
|| fieldEntry.type instanceof GraphQLEnumType
|
|
804
848
|
|| isNonNullOfType(fieldEntry.type, GraphQLScalarType)
|
|
805
849
|
|| isNonNullOfType(fieldEntry.type, GraphQLEnumType)) {
|
|
806
|
-
modelArgs[fieldEntryName] =
|
|
850
|
+
modelArgs[fieldEntryName] = fieldValue;
|
|
807
851
|
} else if (fieldEntry.type instanceof GraphQLObjectType
|
|
808
852
|
|| isNonNullOfType(fieldEntry.type, GraphQLObjectType)) {
|
|
809
853
|
if (fieldEntry.extensions && fieldEntry.extensions.relation) {
|
|
810
854
|
if (!fieldEntry.extensions.relation.embedded) {
|
|
811
|
-
modelArgs[getFieldStorageName(fieldEntryName, fieldEntry)] = adapter.castId(
|
|
855
|
+
modelArgs[getFieldStorageName(fieldEntryName, fieldEntry)] = adapter.castId(fieldValue.id);
|
|
812
856
|
} else {
|
|
813
857
|
const fieldType = fieldEntry.type instanceof GraphQLNonNull
|
|
814
858
|
? fieldEntry.type.ofType : fieldEntry.type;
|
|
815
|
-
modelArgs[fieldEntryName] = (await materializeModel(
|
|
859
|
+
modelArgs[fieldEntryName] = (await materializeModel(fieldValue, fieldType,
|
|
816
860
|
null, operation, session)).modelArgs;
|
|
817
861
|
}
|
|
818
862
|
} else {
|
|
@@ -827,11 +871,11 @@ const materializeModel = async (args, gqltype, linkToParent, operation, session)
|
|
|
827
871
|
if (itemType instanceof GraphQLObjectType && fieldEntry.extensions
|
|
828
872
|
&& fieldEntry.extensions.relation) {
|
|
829
873
|
if (!fieldEntry.extensions.relation.embedded) {
|
|
830
|
-
collectionFields[fieldEntryName] =
|
|
874
|
+
collectionFields[fieldEntryName] = fieldValue;
|
|
831
875
|
} else if (fieldEntry.extensions.relation.embedded) {
|
|
832
876
|
const collectionEntries = [];
|
|
833
877
|
|
|
834
|
-
for (const element of
|
|
878
|
+
for (const element of fieldValue) {
|
|
835
879
|
if (element === null) {
|
|
836
880
|
collectionEntries.push(null);
|
|
837
881
|
} else {
|
|
@@ -843,9 +887,15 @@ const materializeModel = async (args, gqltype, linkToParent, operation, session)
|
|
|
843
887
|
modelArgs[fieldEntryName] = collectionEntries;
|
|
844
888
|
}
|
|
845
889
|
} else if (itemType instanceof GraphQLScalarType || itemType instanceof GraphQLEnumType) {
|
|
846
|
-
modelArgs[fieldEntryName] =
|
|
890
|
+
modelArgs[fieldEntryName] = fieldValue;
|
|
847
891
|
}
|
|
848
892
|
}
|
|
893
|
+
} else if (operation === 'CREATE' && fieldEntryName in Object.prototype && getListShape(fieldEntry.type)
|
|
894
|
+
&& !(fieldEntry.extensions?.relation && !fieldEntry.extensions.relation.embedded)) {
|
|
895
|
+
// A create without a value stores [] for a list: SQL backends store it for every list, and
|
|
896
|
+
// Mongoose defaults to it, except for a list named like an Object.prototype member, where it
|
|
897
|
+
// finds the inherited member. Collections are not stored on the record.
|
|
898
|
+
modelArgs[fieldEntryName] = [];
|
|
849
899
|
}
|
|
850
900
|
}
|
|
851
901
|
|
|
@@ -931,9 +981,9 @@ const completeEmbeddedValue = (fieldName, fieldEntry, value, patch = null) => {
|
|
|
931
981
|
const memberList = getListShape(member.type);
|
|
932
982
|
if (relation && !relation.embedded && memberList) continue;
|
|
933
983
|
const storageName = getFieldStorageName(memberName, member);
|
|
934
|
-
if (item
|
|
984
|
+
if (ownValue(item, storageName) === undefined && memberList) item[storageName] = [];
|
|
935
985
|
if (memberName === 'id' || member.extensions?.readOnly) continue;
|
|
936
|
-
const memberValue = item
|
|
986
|
+
const memberValue = ownValue(item, storageName);
|
|
937
987
|
if (memberValue === null || memberValue === undefined) {
|
|
938
988
|
if (member.type instanceof GraphQLNonNull) {
|
|
939
989
|
throw new SimfinityError(`Required value ${memberName} is missing`, 'REQUIRED_VALUE', 400);
|
|
@@ -979,8 +1029,8 @@ const onUpdateSubject = async (Model, gqltype, controller, args, session, linkTo
|
|
|
979
1029
|
});
|
|
980
1030
|
if (currentObject) {
|
|
981
1031
|
for (const fieldEntryName of embeddedFieldNames) {
|
|
982
|
-
const oldObjectData = currentObject
|
|
983
|
-
const newObjectData = materializedModel.modelArgs
|
|
1032
|
+
const oldObjectData = ownValue(currentObject, fieldEntryName);
|
|
1033
|
+
const newObjectData = ownValue(materializedModel.modelArgs, fieldEntryName);
|
|
984
1034
|
if (newObjectData) {
|
|
985
1035
|
if (Array.isArray(newObjectData)) {
|
|
986
1036
|
materializedModel.modelArgs[fieldEntryName] = newObjectData;
|
|
@@ -988,7 +1038,7 @@ const onUpdateSubject = async (Model, gqltype, controller, args, session, linkTo
|
|
|
988
1038
|
} else {
|
|
989
1039
|
materializedModel.modelArgs[fieldEntryName] = { ...oldObjectData, ...newObjectData };
|
|
990
1040
|
clearNullEmbeddedMembers(materializedModel.modelArgs[fieldEntryName],
|
|
991
|
-
argTypes[fieldEntryName], args
|
|
1041
|
+
argTypes[fieldEntryName], ownValue(args, fieldEntryName));
|
|
992
1042
|
completeEmbeddedValue(fieldEntryName, argTypes[fieldEntryName],
|
|
993
1043
|
materializedModel.modelArgs[fieldEntryName], newObjectData);
|
|
994
1044
|
}
|
|
@@ -1478,6 +1528,9 @@ const resolveById = async (type, args, context, requiredId) => {
|
|
|
1478
1528
|
return results[0] || null;
|
|
1479
1529
|
};
|
|
1480
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.
|
|
1481
1534
|
const buildMutation = (name, includedMutationTypes, includedCustomMutations) => {
|
|
1482
1535
|
const rootQueryArgs = {};
|
|
1483
1536
|
rootQueryArgs.name = name;
|
|
@@ -1494,6 +1547,7 @@ const buildMutation = (name, includedMutationTypes, includedCustomMutations) =>
|
|
|
1494
1547
|
type: type.gqltype,
|
|
1495
1548
|
description: 'add',
|
|
1496
1549
|
args: argsObject,
|
|
1550
|
+
extensions: { simfinityMutation: { typeName: type.gqltype.name, operation: operations.SAVE } },
|
|
1497
1551
|
async resolve(parent, args, context) {
|
|
1498
1552
|
const params = {
|
|
1499
1553
|
type,
|
|
@@ -1511,6 +1565,7 @@ const buildMutation = (name, includedMutationTypes, includedCustomMutations) =>
|
|
|
1511
1565
|
type: type.gqltype,
|
|
1512
1566
|
description: 'delete',
|
|
1513
1567
|
args: { id: { type: new GraphQLNonNull(GraphQLID) } },
|
|
1568
|
+
extensions: { simfinityMutation: { typeName: type.gqltype.name, operation: operations.DELETE } },
|
|
1514
1569
|
async resolve(parent, args, context) {
|
|
1515
1570
|
const params = {
|
|
1516
1571
|
type,
|
|
@@ -1536,6 +1591,7 @@ const buildMutation = (name, includedMutationTypes, includedCustomMutations) =>
|
|
|
1536
1591
|
type: type.gqltype,
|
|
1537
1592
|
description: 'update',
|
|
1538
1593
|
args: argsObject,
|
|
1594
|
+
extensions: { simfinityMutation: { typeName: type.gqltype.name, operation: operations.UPDATE } },
|
|
1539
1595
|
async resolve(parent, args, context) {
|
|
1540
1596
|
const params = {
|
|
1541
1597
|
type,
|
|
@@ -1556,6 +1612,15 @@ const buildMutation = (name, includedMutationTypes, includedCustomMutations) =>
|
|
|
1556
1612
|
type: type.gqltype,
|
|
1557
1613
|
description: actionField.description,
|
|
1558
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
|
+
},
|
|
1559
1624
|
async resolve(parent, args, context) {
|
|
1560
1625
|
const params = {
|
|
1561
1626
|
type,
|
|
@@ -1587,6 +1652,7 @@ const buildMutation = (name, includedMutationTypes, includedCustomMutations) =>
|
|
|
1587
1652
|
type: registeredMutation.outputModel,
|
|
1588
1653
|
description: registeredMutation.description,
|
|
1589
1654
|
args: argsObject,
|
|
1655
|
+
extensions: { simfinityMutation: { operation: operations.CUSTOM_MUTATION } },
|
|
1590
1656
|
async resolve(parent, args, context) {
|
|
1591
1657
|
const params = {
|
|
1592
1658
|
args,
|
|
@@ -1627,8 +1693,10 @@ const buildRootQuery = (name, includedTypes) => {
|
|
|
1627
1693
|
rootQueryArgs.fields[type.listEntitiesEndpointName] = {
|
|
1628
1694
|
type: new GraphQLList(type.gqltype),
|
|
1629
1695
|
args: argsObject,
|
|
1630
|
-
|
|
1631
|
-
|
|
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) {
|
|
1632
1700
|
const joinedScopes = inspectClientPaths(type.gqltype, args, 'find');
|
|
1633
1701
|
const params = {
|
|
1634
1702
|
type,
|
|
@@ -1649,7 +1717,7 @@ const buildRootQuery = (name, includedTypes) => {
|
|
|
1649
1717
|
|
|
1650
1718
|
const [result, resultCount] = await Promise.all([dataPromise, countPromise]);
|
|
1651
1719
|
if (wantsCount) {
|
|
1652
|
-
context
|
|
1720
|
+
reportCount(parent, info, context, resultCount);
|
|
1653
1721
|
}
|
|
1654
1722
|
return result;
|
|
1655
1723
|
},
|
|
@@ -1724,8 +1792,9 @@ const installIdResolver = (gqltype) => {
|
|
|
1724
1792
|
// this runtime: another runtime that reaches a shared type reads the same value through it.
|
|
1725
1793
|
// The value is read as graphql's default resolver reads it, so a method on the parent, such as one a
|
|
1726
1794
|
// custom mutation result supplies, is still called; a promise it returns is read once it settles.
|
|
1795
|
+
// Only a field named like an Object.prototype member that reads as that member reads as null.
|
|
1727
1796
|
const readEmbeddedField = markGenerated((parent, args, context, info) => {
|
|
1728
|
-
const value =
|
|
1797
|
+
const value = readOwnField(parent, args, context, info);
|
|
1729
1798
|
return isThenable(value)
|
|
1730
1799
|
? value.then((resolved) => adapter.readEmbeddedValue(resolved))
|
|
1731
1800
|
: adapter.readEmbeddedValue(value);
|
|
@@ -1744,6 +1813,20 @@ const installEmbeddedValueResolvers = (gqltype, visited) => {
|
|
|
1744
1813
|
}
|
|
1745
1814
|
};
|
|
1746
1815
|
|
|
1816
|
+
// Fields named like Object.prototype members, of registered types and the embedded types they reach,
|
|
1817
|
+
// read through readOwnField when they have no resolver yet; references and collections keep the ones
|
|
1818
|
+
// autoGenerateResolvers gives them.
|
|
1819
|
+
const installObjectMemberResolvers = (gqltype, visited) => {
|
|
1820
|
+
if (!(gqltype instanceof GraphQLObjectType) || visited.has(gqltype)) return;
|
|
1821
|
+
visited.add(gqltype);
|
|
1822
|
+
for (const [fieldName, fieldEntry] of Object.entries(gqltype.getFields())) {
|
|
1823
|
+
const relation = fieldEntry.extensions?.relation;
|
|
1824
|
+
if (relation && !relation.embedded) continue;
|
|
1825
|
+
if (fieldName in Object.prototype && !fieldEntry.resolve) fieldEntry.resolve = readOwnField;
|
|
1826
|
+
if (relation) installObjectMemberResolvers(unwrapListAndNonNull(fieldEntry.type), visited);
|
|
1827
|
+
}
|
|
1828
|
+
};
|
|
1829
|
+
|
|
1747
1830
|
// Configuration issues this runtime already reported, per type, so a repeated createSchema call or a
|
|
1748
1831
|
// type reached from several places logs each one once.
|
|
1749
1832
|
const reportedIssues = new WeakMap();
|
|
@@ -1827,6 +1910,9 @@ const createSchema = (includedQueryTypes, includedMutationTypes, includedCustomM
|
|
|
1827
1910
|
}
|
|
1828
1911
|
}
|
|
1829
1912
|
});
|
|
1913
|
+
// After every type has its relation and embedded value resolvers, which these fields keep.
|
|
1914
|
+
const objectMemberTypes = new Set();
|
|
1915
|
+
Object.values(typesDict.types).forEach(({ gqltype }) => installObjectMemberResolvers(gqltype, objectMemberTypes));
|
|
1830
1916
|
|
|
1831
1917
|
const query = buildRootQuery('RootQueryType', includedQueryTypes);
|
|
1832
1918
|
Object.values(typesDict.types).forEach(({ gqltype }) => {
|
|
@@ -1939,8 +2025,15 @@ const autoGenerateResolvers = (gqltype) => {
|
|
|
1939
2025
|
if (!relatedTypeInfo || !relatedTypeInfo.model) {
|
|
1940
2026
|
throw new Error(`Related type ${relatedType.name} not found or not connected. Make sure it's connected with simfinity.connect() or simfinity.addNoEndpointType().`);
|
|
1941
2027
|
}
|
|
1942
|
-
// Any stored identifier except null or an empty string, so numeric IDs such as 0 resolve.
|
|
1943
|
-
|
|
2028
|
+
// Any stored identifier except null or an empty string, so numeric IDs such as 0 resolve. A
|
|
2029
|
+
// function under the field name is a member, such as the class a MongoDB document inherits
|
|
2030
|
+
// `constructor` from, not an identifier.
|
|
2031
|
+
// The field name is read only when the stored identifier is missing.
|
|
2032
|
+
let relatedId = ownValue(parent, connectionField);
|
|
2033
|
+
if (relatedId == null) {
|
|
2034
|
+
const linked = ownValue(parent, fieldName);
|
|
2035
|
+
relatedId = typeof linked === 'function' ? undefined : linked;
|
|
2036
|
+
}
|
|
1944
2037
|
const id = relatedId?._id ?? relatedId;
|
|
1945
2038
|
if (id == null || id === '') return null;
|
|
1946
2039
|
try {
|
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
|
}
|
package/types/index.d.ts
CHANGED
|
@@ -66,9 +66,12 @@ export class SimfinityError extends Error {
|
|
|
66
66
|
getCode(): string | undefined;
|
|
67
67
|
getStatus(): number | undefined;
|
|
68
68
|
getTimestamp(): string;
|
|
69
|
-
/**
|
|
69
|
+
/**
|
|
70
|
+
* Set on errors that wrap a driver error, such as TRANSACTION_RETRY_EXCEEDED, and on the PostgreSQL database errors
|
|
71
|
+
* that a SQL runtime maps or reports as DATABASE_ERROR; on those PostgreSQL errors it is not enumerable.
|
|
72
|
+
*/
|
|
70
73
|
cause?: unknown;
|
|
71
|
-
/** Set on errors that wrap a driver error, such as
|
|
74
|
+
/** Set on errors that wrap a driver error, such as TRANSACTION_RETRY_EXCEEDED, and on PostgreSQL database errors; returns `cause`. */
|
|
72
75
|
getCause?(): unknown;
|
|
73
76
|
}
|
|
74
77
|
|