@takeshape/schema 11.41.0 → 11.44.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/dist/cjs/src/agents.js +87 -1
- package/dist/cjs/src/index.js +2 -0
- package/dist/cjs/src/relationships.js +86 -1
- package/dist/cjs/src/runtime-schema.js +81 -0
- package/dist/cjs/src/schemas/index.js +3 -7
- package/dist/cjs/src/schemas/project-schema/experimental.json +305 -0
- package/dist/cjs/src/schemas/project-schema/latest.json +1 -1
- package/dist/cjs/src/schemas/project-schema/v3.50.0.json +1 -1
- package/dist/cjs/src/service-dependencies.js +167 -0
- package/dist/cjs/src/util/patch-schema.js +33 -28
- package/dist/cjs/src/validate.js +40 -4
- package/dist/esm/src/agents.js +83 -0
- package/dist/esm/src/index.js +2 -0
- package/dist/esm/src/relationships.js +85 -1
- package/dist/esm/src/runtime-schema.js +73 -0
- package/dist/esm/src/schemas/index.js +0 -2
- package/dist/esm/src/schemas/project-schema/experimental.json +305 -0
- package/dist/esm/src/schemas/project-schema/latest.json +1 -1
- package/dist/esm/src/schemas/project-schema/v3.50.0.json +1 -1
- package/dist/esm/src/service-dependencies.js +159 -0
- package/dist/esm/src/util/patch-schema.js +33 -27
- package/dist/esm/src/validate.js +40 -4
- package/dist/types/src/agents.d.ts +4 -1
- package/dist/types/src/index.d.ts +2 -0
- package/dist/types/src/migration/types.d.ts +1 -3
- package/dist/types/src/project-schema/latest.d.ts +143 -4
- package/dist/types/src/project-schema/v3.48.0.d.ts +143 -4
- package/dist/types/src/project-schema/v3.49.0.d.ts +143 -4
- package/dist/types/src/project-schema/v3.50.0.d.ts +143 -4
- package/dist/types/src/relationships.d.ts +4 -0
- package/dist/types/src/runtime-schema.d.ts +5 -0
- package/dist/types/src/schemas/index.d.ts +0 -2
- package/dist/types/src/service-dependencies.d.ts +13 -0
- package/dist/types/src/types/types.d.ts +1 -0
- package/dist/types/src/util/patch-schema.d.ts +4 -4
- package/dist/types/src/validate.d.ts +8 -3
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/package.json +6 -6
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.resolveSchemaShapeDependencies = exports.collectReferencedShapeNames = void 0;
|
|
7
|
+
const util_1 = require("@takeshape/util");
|
|
8
|
+
const get_js_1 = __importDefault(require("lodash/get.js"));
|
|
9
|
+
const set_js_1 = __importDefault(require("lodash/set.js"));
|
|
10
|
+
const isObject_js_1 = __importDefault(require("lodash/isObject.js"));
|
|
11
|
+
const pick_js_1 = __importDefault(require("lodash/pick.js"));
|
|
12
|
+
const types_1 = require("./types");
|
|
13
|
+
const refs_1 = require("./refs");
|
|
14
|
+
const interfaces_1 = require("./interfaces");
|
|
15
|
+
const schema_util_1 = require("./schema-util");
|
|
16
|
+
function getGraphQLServiceShapeMap(layers) {
|
|
17
|
+
return Object.assign({}, ...Object.values(layers).map(layer => layer.schema?.shapes ?? {}));
|
|
18
|
+
}
|
|
19
|
+
function schemaExtendsShape(context, shapeName, schema) {
|
|
20
|
+
return ((0, types_1.isExtendsSchema)(schema) &&
|
|
21
|
+
schema.extends.some((item) => (0, refs_1.getRefShapeName)(context, item) === shapeName));
|
|
22
|
+
}
|
|
23
|
+
function getMissingPropertyRefs(projectSchema, getNamespace) {
|
|
24
|
+
const propertyRefs = (0, refs_1.getAllPropertyRefs)(projectSchema);
|
|
25
|
+
return propertyRefs.filter(ref => {
|
|
26
|
+
const path = (0, refs_1.propertyRefItemToPath)(getNamespace, ref);
|
|
27
|
+
return !(0, get_js_1.default)(projectSchema, path);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function refToQuery(serviceSchemas, ref) {
|
|
31
|
+
const serviceSchema = serviceSchemas[ref.serviceId].schema;
|
|
32
|
+
if (serviceSchema) {
|
|
33
|
+
if (ref.shapeName === 'Query' || ref.shapeName === 'Mutation') {
|
|
34
|
+
const operation = ref.shapeName === 'Query' ? 'queries' : 'mutations';
|
|
35
|
+
return serviceSchema[operation][ref.propertyName];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function getShapeRefs(services, serviceSchemas, propertyRefs) {
|
|
40
|
+
const results = [];
|
|
41
|
+
for (const ref of propertyRefs) {
|
|
42
|
+
const query = refToQuery(serviceSchemas, ref);
|
|
43
|
+
const layerSchema = serviceSchemas[ref.serviceId]?.schema;
|
|
44
|
+
if (query && layerSchema) {
|
|
45
|
+
const layerWithService = {
|
|
46
|
+
...layerSchema,
|
|
47
|
+
services: (0, pick_js_1.default)(services, [ref.serviceId]) // include own service so refs have isValidService: true
|
|
48
|
+
};
|
|
49
|
+
results.push(...(0, schema_util_1.getAllRefsInQuery)(layerWithService, [], query));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return results;
|
|
53
|
+
}
|
|
54
|
+
function interfacesToShapeNames(projectSchema, shapeRefArray) {
|
|
55
|
+
return shapeRefArray.map(shapeRef => (0, refs_1.refItemToNamespacedShapeName)((0, refs_1.refExpressionToRefItem)(projectSchema, shapeRef)));
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Add all referenced shape names to the refSet meant to be used as a callback to visitSchemaProperties
|
|
59
|
+
*/
|
|
60
|
+
function collectReferencedShapeNames(context, shapeNames) {
|
|
61
|
+
const addRef = (refItem) => {
|
|
62
|
+
shapeNames.add((0, refs_1.refItemToNamespacedShapeName)(refItem));
|
|
63
|
+
};
|
|
64
|
+
const collect = (schema) => {
|
|
65
|
+
const args = schema['@args'];
|
|
66
|
+
if (args && (0, isObject_js_1.default)(args) && (0, types_1.isObjectSchema)(args)) {
|
|
67
|
+
Object.values(args.properties).forEach(collect);
|
|
68
|
+
}
|
|
69
|
+
const argsRefItem = (0, schema_util_1.getArgsReference)(context, schema);
|
|
70
|
+
if (argsRefItem) {
|
|
71
|
+
addRef(argsRefItem);
|
|
72
|
+
}
|
|
73
|
+
const refItem = (0, refs_1.getRef)(context, schema);
|
|
74
|
+
if (refItem) {
|
|
75
|
+
addRef(refItem);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
return collect;
|
|
79
|
+
}
|
|
80
|
+
exports.collectReferencedShapeNames = collectReferencedShapeNames;
|
|
81
|
+
/**
|
|
82
|
+
* Returns a schema with all of its dependencies resolved.
|
|
83
|
+
* SHOULD only return the minimum set of remote dependencies required to enable
|
|
84
|
+
* the local shape definitions.
|
|
85
|
+
*/
|
|
86
|
+
function resolveSchemaShapeDependencies(projectSchema, serviceLayers) {
|
|
87
|
+
const newSchema = (0, util_1.deepClone)(projectSchema);
|
|
88
|
+
if ((0, types_1.isProjectSchemaWithServices)(projectSchema)) {
|
|
89
|
+
const newShapeMap = {};
|
|
90
|
+
const requiredShapeNames = new Set((0, schema_util_1.getAllNamespaceShapes)(projectSchema));
|
|
91
|
+
const getNamespace = (0, refs_1.createGetNamespace)(projectSchema);
|
|
92
|
+
const missingPropertyRefs = getMissingPropertyRefs(projectSchema, getNamespace);
|
|
93
|
+
if (requiredShapeNames.size || missingPropertyRefs.length) {
|
|
94
|
+
const remoteShapeMap = getGraphQLServiceShapeMap(serviceLayers);
|
|
95
|
+
// Add shape references from queries
|
|
96
|
+
(0, util_1.addAll)(requiredShapeNames, getShapeRefs(projectSchema.services, serviceLayers, missingPropertyRefs)
|
|
97
|
+
.filter(schema_util_1.isValidRefItem)
|
|
98
|
+
.map(refs_1.refItemToNamespacedShapeName));
|
|
99
|
+
// Add referenced queries and mutations
|
|
100
|
+
for (const ref of missingPropertyRefs) {
|
|
101
|
+
const query = refToQuery(serviceLayers, ref);
|
|
102
|
+
if (query) {
|
|
103
|
+
(0, set_js_1.default)(newSchema, (0, refs_1.propertyRefItemToPath)(getNamespace, ref), query);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Gather all the remote dependencies that our local schema requires.
|
|
107
|
+
// If the required shape exists in the remote shape map, visit the
|
|
108
|
+
// properties on the remote shape's schema and ensure they are required
|
|
109
|
+
// as well.
|
|
110
|
+
const collectDependencies = (shapeNames) => {
|
|
111
|
+
for (const name of shapeNames) {
|
|
112
|
+
const localShape = newSchema.shapes[name];
|
|
113
|
+
const remoteShape = remoteShapeMap[name];
|
|
114
|
+
// Don't check for dependencies if the shape is already in our schema unless it extends a remote shape
|
|
115
|
+
// getNamespacedShapeNameList already collected those dependencies
|
|
116
|
+
if ((!localShape || (localShape && schemaExtendsShape(projectSchema, name, localShape.schema))) &&
|
|
117
|
+
remoteShape) {
|
|
118
|
+
(0, schema_util_1.visitSchemaProperties)(remoteShape.schema, [remoteShape.name, 'schema'], collectReferencedShapeNames({ services: newSchema.services }, shapeNames));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
collectDependencies(requiredShapeNames);
|
|
123
|
+
// Collect interfaces that requiredShapeNames implement
|
|
124
|
+
const interfaceNames = new Set();
|
|
125
|
+
const requiredShapes = (0, util_1.mapSet)(requiredShapeNames, shapeName => newSchema.shapes[shapeName] ?? remoteShapeMap[shapeName]);
|
|
126
|
+
for (const shape of requiredShapes) {
|
|
127
|
+
if (shape?.interfaces) {
|
|
128
|
+
(0, util_1.addAll)(interfaceNames, interfacesToShapeNames(projectSchema, shape.interfaces));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Collect implementations for interfaces in requiredShapeNames
|
|
132
|
+
const implementationNames = new Set();
|
|
133
|
+
const implementations = (0, interfaces_1.getImplementationShapeNameMap)({ ...newSchema.shapes, ...remoteShapeMap });
|
|
134
|
+
for (const shape of requiredShapes) {
|
|
135
|
+
if ((0, interfaces_1.isInterfaceShape)(shape) && implementations[shape.name]) {
|
|
136
|
+
(0, util_1.addAll)(implementationNames, implementations[shape.name]);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Collect the dependencies for the interfaces and implementations
|
|
140
|
+
collectDependencies(interfaceNames);
|
|
141
|
+
collectDependencies(implementationNames);
|
|
142
|
+
(0, util_1.addAll)(requiredShapeNames, interfaceNames);
|
|
143
|
+
(0, util_1.addAll)(requiredShapeNames, implementationNames);
|
|
144
|
+
// Populate a ShapeMap with the remote shapes we've gathered
|
|
145
|
+
// A combined context which prefers remote shapes for use when merging/inlining
|
|
146
|
+
const mergingContext = { services: newSchema.services, shapes: { ...newSchema.shapes, ...remoteShapeMap } };
|
|
147
|
+
for (const name of requiredShapeNames) {
|
|
148
|
+
const localShape = newSchema.shapes[name];
|
|
149
|
+
const remoteShape = remoteShapeMap[name];
|
|
150
|
+
if (localShape && remoteShape && schemaExtendsShape(mergingContext, name, localShape.schema)) {
|
|
151
|
+
// Merge and inline combined shape when a local shape extends a remote shape
|
|
152
|
+
newShapeMap[name] = {
|
|
153
|
+
...localShape,
|
|
154
|
+
schema: (0, refs_1.dereferenceObjectSchema)(mergingContext, newSchema.shapes[name].schema)
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
else if (localShape ?? remoteShape) {
|
|
158
|
+
newShapeMap[name] = localShape ?? remoteShape;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Assign remote shapes to the new schema, mutating the new schema
|
|
163
|
+
Object.assign(newSchema.shapes, (0, interfaces_1.pruneUnusedInterfaces)(newSchema, newShapeMap));
|
|
164
|
+
}
|
|
165
|
+
return newSchema;
|
|
166
|
+
}
|
|
167
|
+
exports.resolveSchemaShapeDependencies = resolveSchemaShapeDependencies;
|
|
@@ -3,48 +3,53 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.patchSchema =
|
|
7
|
-
const forIn_js_1 = __importDefault(require("lodash/forIn.js"));
|
|
6
|
+
exports.patchSchema = void 0;
|
|
8
7
|
const omit_js_1 = __importDefault(require("lodash/omit.js"));
|
|
9
|
-
const
|
|
8
|
+
const get_js_1 = __importDefault(require("lodash/get.js"));
|
|
9
|
+
const set_js_1 = __importDefault(require("lodash/fp/set.js"));
|
|
10
|
+
const unset_js_1 = __importDefault(require("lodash/unset.js"));
|
|
11
|
+
/**
|
|
12
|
+
* Array of lodash.get paths to keys that are merged when applying a schema update.
|
|
13
|
+
*/
|
|
14
|
+
const schemaUpdateMergedKeys = [
|
|
15
|
+
'queries',
|
|
16
|
+
'mutations',
|
|
17
|
+
'shapes',
|
|
18
|
+
'workflows',
|
|
19
|
+
'forms',
|
|
20
|
+
'services',
|
|
21
|
+
'ai-experimental.agents'
|
|
22
|
+
];
|
|
10
23
|
function shallowMerge(original, update) {
|
|
11
24
|
const result = { ...original };
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
result[key] = obj;
|
|
21
|
-
}
|
|
22
|
-
});
|
|
25
|
+
for (const [key, obj] of Object.entries(update)) {
|
|
26
|
+
// Null is a sentinel value to indicate delete
|
|
27
|
+
if (obj === null) {
|
|
28
|
+
(0, unset_js_1.default)(result, key);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
result[key] = obj;
|
|
32
|
+
}
|
|
23
33
|
}
|
|
24
34
|
return result;
|
|
25
35
|
}
|
|
26
|
-
exports.shallowMerge = shallowMerge;
|
|
27
36
|
/**
|
|
28
|
-
* Apply a schema update to a schema.
|
|
29
|
-
* Resulting schema is not assumed to be valid and should
|
|
37
|
+
* Apply a schema update to a schema. This can operate on a schema with frozen properties,
|
|
38
|
+
* such as one produced by immer. Resulting schema is not assumed to be valid and should
|
|
39
|
+
* be validated if appropriate.
|
|
30
40
|
*/
|
|
31
|
-
function patchSchema(projectSchema,
|
|
41
|
+
function patchSchema(projectSchema, schemaUpdate) {
|
|
32
42
|
// Patch old schema with update
|
|
33
|
-
|
|
43
|
+
let updatedSchema = {
|
|
34
44
|
...projectSchema,
|
|
35
|
-
...(0, omit_js_1.default)(
|
|
45
|
+
...(0, omit_js_1.default)(schemaUpdate, schemaUpdateMergedKeys)
|
|
36
46
|
};
|
|
37
47
|
for (const key of schemaUpdateMergedKeys) {
|
|
38
|
-
|
|
39
|
-
|
|
48
|
+
const update = (0, get_js_1.default)(schemaUpdate, key);
|
|
49
|
+
if (update) {
|
|
50
|
+
updatedSchema = (0, set_js_1.default)(key, shallowMerge((0, get_js_1.default)(projectSchema, key) ?? {}, update), updatedSchema);
|
|
40
51
|
}
|
|
41
52
|
}
|
|
42
|
-
if (update['ai-experimental']?.agents) {
|
|
43
|
-
const ai = {
|
|
44
|
-
agents: shallowMerge(projectSchema['ai-experimental']?.agents, update['ai-experimental'].agents)
|
|
45
|
-
};
|
|
46
|
-
updatedSchema['ai-experimental'] = ai;
|
|
47
|
-
}
|
|
48
53
|
return updatedSchema;
|
|
49
54
|
}
|
|
50
55
|
exports.patchSchema = patchSchema;
|
package/dist/cjs/src/validate.js
CHANGED
|
@@ -141,6 +141,9 @@ function enumerateBasicResolvers(resolver, path) {
|
|
|
141
141
|
visit(resolver, path);
|
|
142
142
|
return results;
|
|
143
143
|
}
|
|
144
|
+
const isValidAgentMutation = (projectSchema, mutationName) => {
|
|
145
|
+
return Boolean(projectSchema['ai-experimental']?.agents?.[mutationName]);
|
|
146
|
+
};
|
|
144
147
|
const validateAIToolConfig = (projectSchema, getNamespace, tool, basePath) => {
|
|
145
148
|
const toolRef = (0, ai_tools_1.getToolRef)(tool);
|
|
146
149
|
const path = typeof tool === 'string' ? basePath : basePath.concat('ref');
|
|
@@ -164,6 +167,9 @@ const validateAIToolConfig = (projectSchema, getNamespace, tool, basePath) => {
|
|
|
164
167
|
};
|
|
165
168
|
}
|
|
166
169
|
const property = (0, get_js_1.default)(projectSchema, propertyPath);
|
|
170
|
+
if (propertyPath[0] === 'mutations' && isValidAgentMutation(projectSchema, propertyPath[1])) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
167
173
|
if (!property) {
|
|
168
174
|
return {
|
|
169
175
|
type: 'notFound',
|
|
@@ -487,8 +493,8 @@ function validateShapeLoaders(context, projectSchema, shape) {
|
|
|
487
493
|
}
|
|
488
494
|
}
|
|
489
495
|
}
|
|
490
|
-
if (shape.cache?.triggers) {
|
|
491
|
-
const min = context.entitlements
|
|
496
|
+
if (shape.cache?.triggers && !context.ignoreEntitlements) {
|
|
497
|
+
const min = context.entitlements.minScheduleTriggerInterval ?? util_1.DEFAULT_MIN_SCHEDULE_TRIGGER_INTERVAL;
|
|
492
498
|
for (let i = 0; i < shape.cache.triggers.length; i++) {
|
|
493
499
|
const trigger = shape.cache.triggers[i];
|
|
494
500
|
if (trigger.type === 'schedule' && trigger.interval < min) {
|
|
@@ -786,14 +792,16 @@ function validateInterfaceImplementations(projectSchema) {
|
|
|
786
792
|
function validateAgents(projectSchema) {
|
|
787
793
|
const getNamespace = (0, refs_1.createGetNamespace)(projectSchema);
|
|
788
794
|
const errors = [];
|
|
795
|
+
const guards = projectSchema['ai-experimental']?.guards;
|
|
789
796
|
const agents = projectSchema['ai-experimental']?.agents;
|
|
790
797
|
if (!agents) {
|
|
791
798
|
return errors;
|
|
792
799
|
}
|
|
793
800
|
for (const [agentName, agent] of Object.entries(agents)) {
|
|
794
801
|
const stateNames = new Set();
|
|
802
|
+
const agentPath = ['ai-experimental', 'agents', agentName];
|
|
795
803
|
for (const [stateId, state] of Object.entries(agent.states)) {
|
|
796
|
-
const statePath = [
|
|
804
|
+
const statePath = [...agentPath, 'states', stateId];
|
|
797
805
|
if (stateNames.has(state.name)) {
|
|
798
806
|
errors.push({
|
|
799
807
|
path: [...statePath, 'name'],
|
|
@@ -819,6 +827,17 @@ function validateAgents(projectSchema) {
|
|
|
819
827
|
});
|
|
820
828
|
}
|
|
821
829
|
}
|
|
830
|
+
if (agent.guards) {
|
|
831
|
+
const invalidGuards = agent.guards.filter(({ guardId }) => !guards?.[guardId]);
|
|
832
|
+
if (invalidGuards.length) {
|
|
833
|
+
const guardPath = [...agentPath, 'guards'];
|
|
834
|
+
errors.push(...invalidGuards.map(({ guardId }, index) => ({
|
|
835
|
+
path: [...guardPath, index, 'guardId'],
|
|
836
|
+
type: 'notFound',
|
|
837
|
+
message: `Invalid guardId "${guardId}"`
|
|
838
|
+
})));
|
|
839
|
+
}
|
|
840
|
+
}
|
|
822
841
|
}
|
|
823
842
|
return errors;
|
|
824
843
|
}
|
|
@@ -906,6 +925,22 @@ function validateStructure(schemaVersion, context, schema, ref) {
|
|
|
906
925
|
}
|
|
907
926
|
return { valid: true, schema: schema, errors: undefined };
|
|
908
927
|
}
|
|
928
|
+
function validateGuards(context, schema) {
|
|
929
|
+
const errors = [];
|
|
930
|
+
if (context.ignoreEntitlements) {
|
|
931
|
+
return errors;
|
|
932
|
+
}
|
|
933
|
+
const numOfGuards = schema['ai-experimental']?.guards ? Object.keys(schema['ai-experimental']?.guards).length : 0;
|
|
934
|
+
const entitledToGuards = context.entitlements.guards ?? 0;
|
|
935
|
+
if (numOfGuards > entitledToGuards) {
|
|
936
|
+
errors.push({
|
|
937
|
+
type: 'entitlement',
|
|
938
|
+
message: `Number of guards exceeds your entitled limit of ${entitledToGuards}`,
|
|
939
|
+
path: ['ai-experimental', 'guards']
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
return errors;
|
|
943
|
+
}
|
|
909
944
|
function formatValidationResult(context, errors, schema) {
|
|
910
945
|
const { suppressErrorPaths } = context;
|
|
911
946
|
if (suppressErrorPaths) {
|
|
@@ -932,7 +967,8 @@ function validateSyntax(context, schema) {
|
|
|
932
967
|
.concat(validateIndexedShapes(context, schema))
|
|
933
968
|
.concat(validateInterfaces(schema))
|
|
934
969
|
.concat(validateInterfaceImplementations(schema))
|
|
935
|
-
.concat(validateAgents(schema))
|
|
970
|
+
.concat(validateAgents(schema))
|
|
971
|
+
.concat(validateGuards(context, schema));
|
|
936
972
|
return formatValidationResult(context, errors, schema);
|
|
937
973
|
}
|
|
938
974
|
async function validateReferences(context, schema) {
|
package/dist/esm/src/agents.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import upperFirst from 'lodash/upperFirst.js';
|
|
2
|
+
import uniq from 'lodash/uniq.js';
|
|
3
|
+
import uniqBy from 'lodash/uniqBy.js';
|
|
2
4
|
export const END_AGENT_EXECUTION = 'endAgentExecution';
|
|
3
5
|
export const BUILT_IN_CHAT_ARGS = [
|
|
4
6
|
{
|
|
@@ -47,3 +49,84 @@ export const getAgentEndStates = (agent, includeSuspend = false) => {
|
|
|
47
49
|
export const getInspectAgentSessionQueryName = (agentName) => {
|
|
48
50
|
return `inspect${upperFirst(agentName)}`;
|
|
49
51
|
};
|
|
52
|
+
export const removeBuiltInArgs = (args) => {
|
|
53
|
+
return args.filter(arg => !BUILT_IN_CHAT_ARG_NAMES.includes(arg.argName));
|
|
54
|
+
};
|
|
55
|
+
export const createArgs = (agent) => {
|
|
56
|
+
let apiArguments = uniqBy(agent.api.arguments ?? [], arg => arg.argName);
|
|
57
|
+
if (agent.api.type === 'chat') {
|
|
58
|
+
apiArguments = removeBuiltInArgs(apiArguments).concat(BUILT_IN_CHAT_ARGS);
|
|
59
|
+
}
|
|
60
|
+
return apiArguments.length > 0
|
|
61
|
+
? {
|
|
62
|
+
type: 'object',
|
|
63
|
+
properties: apiArguments.reduce((acc, argument) => {
|
|
64
|
+
acc[argument.argName] = {
|
|
65
|
+
type: argument.argType === 'sessionId' ? 'string' : argument.argType
|
|
66
|
+
};
|
|
67
|
+
return acc;
|
|
68
|
+
}, {}),
|
|
69
|
+
required: apiArguments.filter(arg => arg.required).map(arg => arg.argName)
|
|
70
|
+
}
|
|
71
|
+
: undefined;
|
|
72
|
+
};
|
|
73
|
+
export function addAiQueries(projectSchema) {
|
|
74
|
+
const agents = projectSchema['ai-experimental']?.agents;
|
|
75
|
+
if (!agents) {
|
|
76
|
+
return projectSchema;
|
|
77
|
+
}
|
|
78
|
+
const newSchema = {
|
|
79
|
+
...projectSchema,
|
|
80
|
+
queries: { ...projectSchema.queries },
|
|
81
|
+
mutations: { ...projectSchema.mutations }
|
|
82
|
+
};
|
|
83
|
+
for (const [agentName, agent] of Object.entries(agents)) {
|
|
84
|
+
// Get valid return types based on states that could possibly be the end state
|
|
85
|
+
// TODO In the future the agent will have a return type defined and we will instead validate
|
|
86
|
+
// that all the return states will return the correct shape.
|
|
87
|
+
const returnStates = [...getAgentEndStates(agent, true)];
|
|
88
|
+
const returnTypes = uniq(returnStates.map(stateId => {
|
|
89
|
+
const { execution } = agent.states[stateId];
|
|
90
|
+
if (execution.type === 'chat') {
|
|
91
|
+
return 'TSChatResponse';
|
|
92
|
+
}
|
|
93
|
+
if (execution.type === 'generate') {
|
|
94
|
+
return execution.outputShape ? execution.outputShape : 'string';
|
|
95
|
+
}
|
|
96
|
+
return 'JSON';
|
|
97
|
+
}));
|
|
98
|
+
const shape = returnTypes.length === 0 ? 'string' : returnTypes.length === 1 ? returnTypes[0] : 'JSON';
|
|
99
|
+
if (newSchema.mutations[agentName] !== undefined) {
|
|
100
|
+
throw new Error(`Schema already has a mutation with the name ${agentName}`);
|
|
101
|
+
}
|
|
102
|
+
newSchema.mutations[agentName] = {
|
|
103
|
+
description: agent.description,
|
|
104
|
+
args: createArgs(agent),
|
|
105
|
+
shape,
|
|
106
|
+
resolver: {
|
|
107
|
+
name: 'ai:runAgent',
|
|
108
|
+
agentName
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
if (shape === 'TSChatResponse') {
|
|
112
|
+
newSchema.queries[getInspectAgentSessionQueryName(agentName)] ||= {
|
|
113
|
+
description: `Inspect a session for the ${agentName} agent`,
|
|
114
|
+
args: {
|
|
115
|
+
type: 'object',
|
|
116
|
+
properties: {
|
|
117
|
+
sessionId: {
|
|
118
|
+
type: 'string'
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
required: ['sessionId']
|
|
122
|
+
},
|
|
123
|
+
shape: 'TSAgentSession',
|
|
124
|
+
resolver: {
|
|
125
|
+
name: 'ai:inspectAgentSession',
|
|
126
|
+
agentName
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return newSchema;
|
|
132
|
+
}
|
package/dist/esm/src/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { isDefined } from "@takeshape/util";
|
|
2
2
|
import find from "lodash/find.js";
|
|
3
|
-
import
|
|
3
|
+
import camelCase from "lodash/camelCase.js";
|
|
4
|
+
import uniq from "lodash/uniq.js";
|
|
5
|
+
import { isPropertySchemaWithRelationship, isModelShape, isObjectSchema } from "./types/index.js";
|
|
4
6
|
import { getRefShapeName, followRef } from "./refs.js";
|
|
5
7
|
import { isUnionSchema } from "./unions.js";
|
|
6
8
|
import { builtInShapes } from "./builtin-schema.js";
|
|
@@ -220,3 +222,85 @@ export function hasUnequalRelationships(relationships) {
|
|
|
220
222
|
return nextPropertySchema ? !isEqualRelationship(propertySchema, nextPropertySchema) : false;
|
|
221
223
|
}));
|
|
222
224
|
}
|
|
225
|
+
function getRelatedShapeIds(relationships) {
|
|
226
|
+
return uniq(relationships.map(rel => (rel.hasBackreference ? rel.shapeId : undefined)).filter(isDefined));
|
|
227
|
+
}
|
|
228
|
+
function getShapes(projectSchema, shapeIds) {
|
|
229
|
+
return shapeIds.map(shapeId => getShapeById(projectSchema, shapeId)).filter(isDefined);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Adds backreference fields to the schema.
|
|
233
|
+
*/
|
|
234
|
+
export function addRelatedFields(projectSchema, allRelationships) {
|
|
235
|
+
for (const [shapeId, shapeRelationships] of Object.entries(allRelationships)) {
|
|
236
|
+
const shape = getShapeById(projectSchema, shapeId);
|
|
237
|
+
if (shape && isObjectSchema(shape.schema)) {
|
|
238
|
+
const relatedShapeIds = getRelatedShapeIds(shapeRelationships);
|
|
239
|
+
const relatedShapeNames = getShapes(projectSchema, relatedShapeIds).map(shape => shape.name);
|
|
240
|
+
if (relatedShapeNames.length) {
|
|
241
|
+
let shapeName;
|
|
242
|
+
if (relatedShapeNames.length === 1) {
|
|
243
|
+
// If only one back reference exists, _references is a regular shape
|
|
244
|
+
shapeName = relatedShapeNames[0];
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
// If many back references exist, _references will be a union of referring shapes
|
|
248
|
+
shapeName = `${shape.name}Reference`;
|
|
249
|
+
projectSchema.shapes[shapeName] = {
|
|
250
|
+
id: shapeName,
|
|
251
|
+
name: shapeName,
|
|
252
|
+
title: shapeName,
|
|
253
|
+
schema: {
|
|
254
|
+
oneOf: relatedShapeNames.map(name => ({ "@ref": `local:${name}` }))
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
// _references, a list of all backreferences across all fields on this shape.
|
|
259
|
+
// It lists all the items that have references to the current item.
|
|
260
|
+
// It is for convenience.
|
|
261
|
+
if (shapeRelationships.some(rel => rel.hasBackreference)) {
|
|
262
|
+
shape.schema.properties._references = {
|
|
263
|
+
"@args": `TSListArgs<local:${shapeName}>`,
|
|
264
|
+
"@ref": `PaginatedList<local:${shapeName}>`,
|
|
265
|
+
"@resolver": {
|
|
266
|
+
name: "shapedb:list",
|
|
267
|
+
service: "shapedb",
|
|
268
|
+
args: {
|
|
269
|
+
ops: [
|
|
270
|
+
{ path: "$", mapping: "$args" },
|
|
271
|
+
{ path: `baseWhere._references.eq`, mapping: "$source._id" },
|
|
272
|
+
{ path: `baseWhere._shapeId.in`, value: relatedShapeIds }
|
|
273
|
+
]
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// Create a schema for the backreference on the referred-to shape
|
|
280
|
+
for (const relationship of shapeRelationships) {
|
|
281
|
+
const relatedShape = getShapeById(projectSchema, relationship.shapeId);
|
|
282
|
+
if (relatedShape && relationship.hasBackreference) {
|
|
283
|
+
const { relatedName } = relationship;
|
|
284
|
+
const relatedFieldName = relatedName ? relatedName : `${camelCase(relatedShape.name)}Set`;
|
|
285
|
+
const filterField = relatedName ? relationship.path.concat("_id").join(".") : "_references";
|
|
286
|
+
shape.schema.properties[relatedFieldName] = {
|
|
287
|
+
"@args": `TSListArgs<local:${relatedShape.name}>`,
|
|
288
|
+
"@ref": `PaginatedList<local:${relatedShape.name}>`,
|
|
289
|
+
"@resolver": {
|
|
290
|
+
name: "shapedb:list",
|
|
291
|
+
service: "shapedb",
|
|
292
|
+
args: {
|
|
293
|
+
ops: [
|
|
294
|
+
{ path: "$", mapping: "$args" },
|
|
295
|
+
{ path: `baseWhere.${filterField}.eq`, mapping: "$source._id" },
|
|
296
|
+
{ path: `baseWhere._shapeId.eq`, value: relatedShape.id }
|
|
297
|
+
]
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return projectSchema;
|
|
306
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { SchemaBuildError } from "@takeshape/errors";
|
|
2
|
+
import compose from "lodash/fp/compose.js";
|
|
3
|
+
import { deepClone } from "@takeshape/util";
|
|
4
|
+
import set from "lodash/set.js";
|
|
5
|
+
import isError from "lodash/isError.js";
|
|
6
|
+
import { isModelShape } from "./types/index.js";
|
|
7
|
+
import { applyDefaultsToSchema, createShape } from "./schema-util.js";
|
|
8
|
+
import { addRelatedFields, findExistingRelationships } from "./relationships.js";
|
|
9
|
+
import { flattenTemplates } from "./flatten-templates.js";
|
|
10
|
+
import { addAiQueries } from "./agents.js";
|
|
11
|
+
import { resolveSchemaShapeDependencies } from "./service-dependencies.js";
|
|
12
|
+
export function applyLegacyCompatibilityTweaks(projectSchema) {
|
|
13
|
+
const newSchema = deepClone(projectSchema);
|
|
14
|
+
let hasSearchableShapes = false;
|
|
15
|
+
for (const [shapeName, shape] of Object.entries(newSchema.shapes)) {
|
|
16
|
+
if (isModelShape(shape)) {
|
|
17
|
+
hasSearchableShapes = true;
|
|
18
|
+
// Magic _contentTypeId field used for V1 compatibility
|
|
19
|
+
set(shape, ["schema", "properties", "_contentTypeId"], { type: "string" });
|
|
20
|
+
set(shape, ["schema", "properties", "_contentTypeName"], { type: "string" });
|
|
21
|
+
if (shape.model?.type !== "single") {
|
|
22
|
+
newSchema.queries[`search${shapeName}Index`] = {
|
|
23
|
+
shape: `SearchResults<${shapeName}>`,
|
|
24
|
+
resolver: {
|
|
25
|
+
name: "takeshape:search",
|
|
26
|
+
service: "takeshape",
|
|
27
|
+
shapeName
|
|
28
|
+
},
|
|
29
|
+
args: `TSSearchArgs<${shapeName}>`
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (shapeName === "Asset") {
|
|
34
|
+
// Magic s3Key field to provide V1 compatibility with old projects until the field is
|
|
35
|
+
// formally removed and users are notified
|
|
36
|
+
set(shape, ["schema", "properties", "s3Key"], {
|
|
37
|
+
title: "s3 key",
|
|
38
|
+
type: "string",
|
|
39
|
+
"@mapping": "shapedb:Asset.Hk6FQuz5",
|
|
40
|
+
"@deprecationReason": "Use path instead"
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (hasSearchableShapes) {
|
|
45
|
+
// A placeholder shape for the TSSearchable interface
|
|
46
|
+
newSchema.shapes.TSSearchable = createShape("TSSearchable", { type: "object", properties: {} });
|
|
47
|
+
newSchema.queries.search = {
|
|
48
|
+
shape: "SearchResults<TSSearchable>",
|
|
49
|
+
resolver: {
|
|
50
|
+
name: "takeshape:search",
|
|
51
|
+
service: "takeshape"
|
|
52
|
+
},
|
|
53
|
+
args: "TSSearchArgs<TSSearchable>"
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
addRelatedFields(newSchema, findExistingRelationships(projectSchema, projectSchema.shapes));
|
|
57
|
+
return newSchema;
|
|
58
|
+
}
|
|
59
|
+
export const applyDefaultsAndFlatten = compose(flattenTemplates, applyLegacyCompatibilityTweaks, applyDefaultsToSchema, addAiQueries);
|
|
60
|
+
export function buildRuntimeSchema(projectSchema, serviceLayers, log) {
|
|
61
|
+
try {
|
|
62
|
+
const projectSchemaWithDependencies = resolveSchemaShapeDependencies(projectSchema, serviceLayers);
|
|
63
|
+
return applyDefaultsAndFlatten(projectSchemaWithDependencies);
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
const error = new SchemaBuildError("An error occurred while building the schema", {
|
|
67
|
+
cause: isError(err) ? err : undefined,
|
|
68
|
+
schema: projectSchema
|
|
69
|
+
});
|
|
70
|
+
log("build runtime error", error);
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
export const CURRENT_SCHEMA_VERSION = '3.50.0';
|
|
3
3
|
export { default as anyProjectSchema } from './project-schema.json';
|
|
4
4
|
export { default as latestSchemaJson } from './project-schema/v3.50.0.json';
|
|
5
|
-
export { default as authSchemaJson } from './auth-schemas.json';
|
|
6
|
-
export { default as experimentalSchemaJson } from './project-schema/experimental.json';
|
|
7
5
|
import experimentalSchemaJson from './project-schema/experimental.json';
|
|
8
6
|
import metaSchemaV1_0_0 from './project-schema/meta-schema-v1.0.0.json';
|
|
9
7
|
import projectSchemaV1_0_0 from './project-schema/v1.0.0.json';
|