@exellix/graphs-studio-data-flow 0.1.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/package.json +33 -0
- package/src/graphContractMetadata.js +484 -0
- package/src/index.js +8 -0
- package/src/informationFlow.js +225 -0
- package/src/informationFlowFocus.js +332 -0
- package/src/informationFlowLayerDocument.js +69 -0
- package/src/informationFlowOutputSurface.js +339 -0
- package/src/ioLinking.js +236 -0
- package/src/lib/flatRuntimeInput.js +38 -0
- package/src/lib/memorixEntityContentTypes.js +116 -0
- package/src/lib/memorixScopedConfig.js +108 -0
- package/src/lib/nodeMetadataAccessors.js +59 -0
- package/src/lib/recordEligibilityRules.js +542 -0
- package/src/lib/recordFiltersJsonConditionsBridge.js +97 -0
- package/src/lib/taskNodeConfiguration.js +117 -0
- package/src/lib/webQueryTemplate.js +277 -0
- package/src/pathClassification.js +133 -0
- package/src/planning.js +3 -0
- package/src/planningSourceFamily.js +109 -0
- package/src/types.ts +246 -0
- package/src/webScopingPlanning.js +131 -0
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@exellix/graphs-studio-data-flow",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Information Flow (data-flow) layer helpers for Graphs Studio.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=20.19.0 || >=22.12.0"
|
|
8
|
+
},
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public",
|
|
11
|
+
"registry": "https://registry.npmjs.org/"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+ssh://git@github.com/exellix/graphs-studio.git",
|
|
16
|
+
"directory": "packages/graphs-studio-data-flow"
|
|
17
|
+
},
|
|
18
|
+
"main": "./src/index.js",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": "./src/index.js",
|
|
21
|
+
"./planning": "./src/planning.js",
|
|
22
|
+
"./lib/memorixScopedConfig.js": "./src/lib/memorixScopedConfig.js"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"src"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"prepublishOnly": "node --test tests/graphs-studio-data-flow.test.mjs"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@exellix/graph-engine": "^9.2.3"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* View-model + patch helpers for metadata.graphEntry / metadata.graphResponse.
|
|
3
|
+
* Prefers request/response field names; falls back to legacy execution/finalOutput keys.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { normalizeConditionsDataFilters } from './lib/recordEligibilityRules.js';
|
|
7
|
+
import { normalizeGraphEntryInputTypes } from './lib/memorixEntityContentTypes.js';
|
|
8
|
+
import { normalizeFlatRuntimeInput } from './lib/flatRuntimeInput.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Default example body format from semantic input `kind` (authoring hint only).
|
|
12
|
+
* @param {string} kind
|
|
13
|
+
* @returns {import('../types/graphContracts').GraphEntryExampleFormat}
|
|
14
|
+
*/
|
|
15
|
+
export function inferExampleFormatForInputKind(kind) {
|
|
16
|
+
const k = String(kind).toLowerCase();
|
|
17
|
+
if (k === 'content') return 'markdown';
|
|
18
|
+
if (k === 'query' || k === 'user-input' || k === 'text') return 'text';
|
|
19
|
+
if (k === 'record' || k === 'metadata') return 'json';
|
|
20
|
+
return 'json';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Normalize `metadata.graphEntry.exampleInput` (graph-level sample).
|
|
25
|
+
* @param {unknown} ge `graph.metadata.graphEntry`
|
|
26
|
+
* @returns {import('../types/graphContracts').GraphEntryExampleInput}
|
|
27
|
+
*/
|
|
28
|
+
export function normalizeGraphEntryExampleInput(ge) {
|
|
29
|
+
if (!ge || typeof ge !== 'object') return { format: 'json', value: '' };
|
|
30
|
+
const ex = /** @type {Record<string, unknown>} */ (ge).exampleInput;
|
|
31
|
+
if (!ex || typeof ex !== 'object') return { format: 'json', value: '' };
|
|
32
|
+
const fmt = ex.format === 'markdown' || ex.format === 'text' || ex.format === 'json' ? ex.format : 'json';
|
|
33
|
+
const value = typeof ex.value === 'string' ? ex.value : '';
|
|
34
|
+
const title = typeof ex.title === 'string' ? ex.title : undefined;
|
|
35
|
+
return title ? { format: fmt, value, title } : { format: fmt, value };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Normalize plural graph-entry examples. Falls back to the legacy single `exampleInput`.
|
|
40
|
+
* @param {unknown} ge `graph.metadata.graphEntry`
|
|
41
|
+
* @returns {import('../types/graphContracts').GraphEntryExampleInput[]}
|
|
42
|
+
*/
|
|
43
|
+
export function normalizeGraphEntryExampleInputs(ge) {
|
|
44
|
+
if (!ge || typeof ge !== 'object') return [];
|
|
45
|
+
const raw = /** @type {Record<string, unknown>} */ (ge).exampleInputs;
|
|
46
|
+
if (Array.isArray(raw)) {
|
|
47
|
+
return raw
|
|
48
|
+
.map((item) => {
|
|
49
|
+
if (!item || typeof item !== 'object' || Array.isArray(item)) return null;
|
|
50
|
+
const o = /** @type {Record<string, unknown>} */ (item);
|
|
51
|
+
const fmt = o.format === 'markdown' || o.format === 'text' || o.format === 'json' ? o.format : 'json';
|
|
52
|
+
const value = typeof o.value === 'string' ? o.value : '';
|
|
53
|
+
const title = typeof o.title === 'string' ? o.title.trim() : '';
|
|
54
|
+
return title ? { format: fmt, value, title } : { format: fmt, value };
|
|
55
|
+
})
|
|
56
|
+
.filter(Boolean);
|
|
57
|
+
}
|
|
58
|
+
const single = normalizeGraphEntryExampleInput(ge);
|
|
59
|
+
if (single.value.trim()) return [single];
|
|
60
|
+
|
|
61
|
+
const src = /** @type {Record<string, unknown>} */ (ge);
|
|
62
|
+
const legacy = src.requestExample !== undefined ? src.requestExample : src.executionExample;
|
|
63
|
+
if (legacy != null && typeof legacy === 'object' && !Array.isArray(legacy)) {
|
|
64
|
+
const flat = normalizeFlatRuntimeInput(/** @type {Record<string, unknown>} */ (legacy));
|
|
65
|
+
if (Object.keys(flat).length > 0) {
|
|
66
|
+
return [{ format: 'json', value: JSON.stringify(flat) }];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Normalize plural graph-response final output examples. Falls back to the legacy
|
|
74
|
+
* single `responseExample` / `finalOutputExample` object.
|
|
75
|
+
* @param {unknown} gr `graph.metadata.graphResponse`
|
|
76
|
+
* @returns {import('../types/graphContracts').GraphResponseOutputExample[]}
|
|
77
|
+
*/
|
|
78
|
+
export function normalizeGraphResponseOutputExamples(gr) {
|
|
79
|
+
if (!gr || typeof gr !== 'object') return [];
|
|
80
|
+
const src = /** @type {Record<string, unknown>} */ (gr);
|
|
81
|
+
const raw = src.outputExamples;
|
|
82
|
+
if (Array.isArray(raw)) {
|
|
83
|
+
return raw
|
|
84
|
+
.map((item) => {
|
|
85
|
+
if (!item || typeof item !== 'object' || Array.isArray(item)) return null;
|
|
86
|
+
const o = /** @type {Record<string, unknown>} */ (item);
|
|
87
|
+
const fmt = o.format === 'markdown' || o.format === 'text' || o.format === 'json' ? o.format : 'json';
|
|
88
|
+
const value = typeof o.value === 'string' ? o.value : '';
|
|
89
|
+
const title = typeof o.title === 'string' ? o.title.trim() : '';
|
|
90
|
+
return title ? { format: fmt, value, title } : { format: fmt, value };
|
|
91
|
+
})
|
|
92
|
+
.filter(Boolean);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const single = src.responseExample !== undefined ? src.responseExample : src.finalOutputExample;
|
|
96
|
+
if (single === undefined) return [];
|
|
97
|
+
try {
|
|
98
|
+
return [{ format: 'json', value: JSON.stringify(single, null, 2) }];
|
|
99
|
+
} catch {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Normalize data-level filters stored on `metadata.graphEntry.inputs[]`.
|
|
106
|
+
* Keys may be authored as `field` or `data.field`; persisted keys are relative to `data`.
|
|
107
|
+
* @param {unknown} value
|
|
108
|
+
* @returns {Record<string, unknown>|undefined}
|
|
109
|
+
*/
|
|
110
|
+
export function normalizeGraphEntryDataFilters(value) {
|
|
111
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;
|
|
112
|
+
/** @type {Record<string, unknown>} */
|
|
113
|
+
const out = {};
|
|
114
|
+
for (const [rawKey, rawValue] of Object.entries(/** @type {Record<string, unknown>} */ (value))) {
|
|
115
|
+
const key = String(rawKey)
|
|
116
|
+
.trim()
|
|
117
|
+
.replace(/^data\./, '');
|
|
118
|
+
if (!key || !/^[\w.]+$/.test(key)) continue;
|
|
119
|
+
const valueType = typeof rawValue;
|
|
120
|
+
if (rawValue == null) continue;
|
|
121
|
+
if (valueType === 'string' || valueType === 'number' || valueType === 'boolean') {
|
|
122
|
+
out[key] = rawValue;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (Array.isArray(rawValue) && rawValue.every((v) => typeof v === 'string' || typeof v === 'number')) {
|
|
126
|
+
out[key] = rawValue;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (rawValue && typeof rawValue === 'object' && !Array.isArray(rawValue)) {
|
|
130
|
+
/** @type {Record<string, unknown>} */
|
|
131
|
+
const ops = {};
|
|
132
|
+
for (const op of ['$gte', '$lte', '$gt', '$lt', '$eq', '$ne']) {
|
|
133
|
+
const opValue = /** @type {Record<string, unknown>} */ (rawValue)[op];
|
|
134
|
+
if (opValue == null) continue;
|
|
135
|
+
if (
|
|
136
|
+
typeof opValue === 'string' ||
|
|
137
|
+
typeof opValue === 'number' ||
|
|
138
|
+
typeof opValue === 'boolean' ||
|
|
139
|
+
(Array.isArray(opValue) && opValue.every((v) => typeof v === 'string' || typeof v === 'number'))
|
|
140
|
+
) {
|
|
141
|
+
ops[op] = opValue;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (Object.keys(ops).length) out[key] = ops;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return Object.keys(out).length ? out : undefined;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Infer graph-entry record slots from task-node `inputsConfig.record` bindings.
|
|
152
|
+
*
|
|
153
|
+
* @param {unknown} nodes
|
|
154
|
+
* @returns {import('../types/graphContracts').GraphEntryInputSpec[]}
|
|
155
|
+
*/
|
|
156
|
+
export function inferGraphEntryInputsFromTaskNodes(nodes) {
|
|
157
|
+
if (!Array.isArray(nodes)) return [];
|
|
158
|
+
/** @type {Map<string, import('../types/graphContracts').GraphEntryInputSpec>} */
|
|
159
|
+
const byPath = new Map();
|
|
160
|
+
for (const node of nodes) {
|
|
161
|
+
if (!node || typeof node !== 'object' || Array.isArray(node) || /** @type {{ type?: unknown }} */ (node).type !== 'task') {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
const inputsConfig = /** @type {{ record?: unknown }} */ (node).inputsConfig;
|
|
165
|
+
if (!inputsConfig || typeof inputsConfig !== 'object' || Array.isArray(inputsConfig)) continue;
|
|
166
|
+
const record = /** @type {{ type?: unknown; path?: unknown }} */ (inputsConfig).record;
|
|
167
|
+
if (!record || typeof record !== 'object' || Array.isArray(record)) continue;
|
|
168
|
+
if (record.type !== 'executionMemoryPath') continue;
|
|
169
|
+
const path = typeof record.path === 'string' ? record.path.trim() : '';
|
|
170
|
+
if (!path || path === 'input.raw') continue;
|
|
171
|
+
if (!byPath.has(path)) {
|
|
172
|
+
byPath.set(path, { kind: 'record', path, required: true });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return [...byPath.values()];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Effective graph-entry contract for simulate/run: persisted `metadata.graphEntry`
|
|
180
|
+
* plus inferred record slots from task nodes when the executable graph omits them.
|
|
181
|
+
*
|
|
182
|
+
* @param {unknown} graph
|
|
183
|
+
* @returns {Record<string, unknown>}
|
|
184
|
+
*/
|
|
185
|
+
export function resolveEffectiveGraphEntry(graph) {
|
|
186
|
+
const g = graph && typeof graph === 'object' && !Array.isArray(graph) ? /** @type {Record<string, unknown>} */ (graph) : {};
|
|
187
|
+
const md =
|
|
188
|
+
g.metadata && typeof g.metadata === 'object' && !Array.isArray(g.metadata)
|
|
189
|
+
? /** @type {Record<string, unknown>} */ (g.metadata)
|
|
190
|
+
: {};
|
|
191
|
+
const ge =
|
|
192
|
+
md.graphEntry && typeof md.graphEntry === 'object' && !Array.isArray(md.graphEntry)
|
|
193
|
+
? { .../** @type {Record<string, unknown>} */ (md.graphEntry) }
|
|
194
|
+
: {};
|
|
195
|
+
if (normalizeGraphEntryInputs(ge).length === 0) {
|
|
196
|
+
const inferred = inferGraphEntryInputsFromTaskNodes(g.nodes);
|
|
197
|
+
if (inferred.length > 0) ge.inputs = inferred;
|
|
198
|
+
}
|
|
199
|
+
return ge;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Ensure working metadata exposes a graph-entry input contract for Design/Simulate UI.
|
|
204
|
+
*
|
|
205
|
+
* @param {unknown} metadata
|
|
206
|
+
* @param {unknown} nodes
|
|
207
|
+
* @returns {Record<string, unknown>}
|
|
208
|
+
*/
|
|
209
|
+
export function ensureGraphEntryInputsOnMetadata(metadata, nodes) {
|
|
210
|
+
const md = metadata && typeof metadata === 'object' && !Array.isArray(metadata) ? { ...metadata } : {};
|
|
211
|
+
const ge =
|
|
212
|
+
md.graphEntry && typeof md.graphEntry === 'object' && !Array.isArray(md.graphEntry)
|
|
213
|
+
? { .../** @type {Record<string, unknown>} */ (md.graphEntry) }
|
|
214
|
+
: {};
|
|
215
|
+
if (normalizeGraphEntryInputs(ge).length === 0) {
|
|
216
|
+
const inferred = inferGraphEntryInputsFromTaskNodes(nodes);
|
|
217
|
+
if (inferred.length > 0) ge.inputs = inferred;
|
|
218
|
+
}
|
|
219
|
+
if (Object.keys(ge).length > 0) md.graphEntry = ge;
|
|
220
|
+
else delete md.graphEntry;
|
|
221
|
+
return md;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Normalize `metadata.graphEntry.inputs` from persisted graph JSON (no aliases).
|
|
226
|
+
* @param {unknown} ge `graph.metadata.graphEntry`
|
|
227
|
+
* @returns {import('../types/graphContracts').GraphEntryInputSpec[]}
|
|
228
|
+
*/
|
|
229
|
+
export function normalizeGraphEntryInputs(ge) {
|
|
230
|
+
if (!ge || typeof ge !== 'object') return [];
|
|
231
|
+
const raw = /** @type {{ inputs?: unknown }} */ (ge).inputs;
|
|
232
|
+
if (!Array.isArray(raw)) return [];
|
|
233
|
+
/** @type {import('../types/graphContracts').GraphEntryInputSpec[]} */
|
|
234
|
+
const out = [];
|
|
235
|
+
for (const item of raw) {
|
|
236
|
+
if (!item || typeof item !== 'object') continue;
|
|
237
|
+
const o = /** @type {Record<string, unknown>} */ (item);
|
|
238
|
+
const kind = typeof o.kind === 'string' ? o.kind.trim() : '';
|
|
239
|
+
const path = typeof o.path === 'string' ? o.path.trim() : '';
|
|
240
|
+
const hasSelection = ['entityIds', 'inputTypes', 'narrativeTypeIds', 'scopingQuestionIds'].some((key) => {
|
|
241
|
+
const rawList = o[key];
|
|
242
|
+
return Array.isArray(rawList) && rawList.some((v) => typeof v === 'string' && v.trim());
|
|
243
|
+
});
|
|
244
|
+
if (!kind && !path && !hasSelection) continue;
|
|
245
|
+
/** @type {import('../types/graphContracts').GraphEntryInputSpec} */
|
|
246
|
+
const spec = {
|
|
247
|
+
kind,
|
|
248
|
+
path,
|
|
249
|
+
required: Boolean(o.required),
|
|
250
|
+
};
|
|
251
|
+
if (o.cardinality === 'many' || o.cardinality === 'one') spec.cardinality = o.cardinality;
|
|
252
|
+
for (const key of ['entityIds', 'narrativeTypeIds', 'scopingQuestionIds']) {
|
|
253
|
+
const rawList = o[key];
|
|
254
|
+
if (Array.isArray(rawList)) {
|
|
255
|
+
const list = rawList.map((v) => (typeof v === 'string' ? v.trim() : '')).filter(Boolean);
|
|
256
|
+
if (list.length) spec[key] = list;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const inputTypes = normalizeGraphEntryInputTypes(o.inputTypes);
|
|
260
|
+
if (inputTypes) spec.inputTypes = inputTypes;
|
|
261
|
+
if (typeof o.name === 'string' && o.name.trim()) spec.name = o.name.trim();
|
|
262
|
+
if (typeof o.title === 'string' && o.title.trim()) spec.title = o.title.trim();
|
|
263
|
+
if (typeof o.description === 'string' && o.description.trim()) spec.description = o.description.trim();
|
|
264
|
+
const dataFilters = normalizeGraphEntryDataFilters(o.dataFilters);
|
|
265
|
+
if (dataFilters) spec.dataFilters = dataFilters;
|
|
266
|
+
if ('schema' in o) spec.schema = o.schema;
|
|
267
|
+
const example = typeof o.example === 'string' ? o.example : '';
|
|
268
|
+
const exFmt =
|
|
269
|
+
o.exampleFormat === 'markdown' || o.exampleFormat === 'text' || o.exampleFormat === 'json'
|
|
270
|
+
? o.exampleFormat
|
|
271
|
+
: undefined;
|
|
272
|
+
if (example.trim()) {
|
|
273
|
+
spec.example = example.trim();
|
|
274
|
+
spec.exampleFormat = exFmt ?? inferExampleFormatForInputKind(kind);
|
|
275
|
+
} else if (exFmt) {
|
|
276
|
+
spec.exampleFormat = exFmt;
|
|
277
|
+
}
|
|
278
|
+
out.push(spec);
|
|
279
|
+
}
|
|
280
|
+
return out;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Normalize `metadata.graphEntry.conditions` (narratives, record gates, `aiCondition`, catalog ids).
|
|
285
|
+
*
|
|
286
|
+
* @param {unknown} raw
|
|
287
|
+
* @returns {import('../types/graphContracts').GraphEntryConditions | undefined}
|
|
288
|
+
*/
|
|
289
|
+
export function normalizeGraphEntryConditions(raw) {
|
|
290
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return undefined;
|
|
291
|
+
const src = /** @type {Record<string, unknown>} */ (raw);
|
|
292
|
+
const narratives = Array.isArray(src.narratives)
|
|
293
|
+
? src.narratives.map((x) => String(x).trim()).filter(Boolean)
|
|
294
|
+
: [];
|
|
295
|
+
let aiCondition;
|
|
296
|
+
if (src.aiCondition && typeof src.aiCondition === 'object' && !Array.isArray(src.aiCondition)) {
|
|
297
|
+
const ac = /** @type {Record<string, unknown>} */ (src.aiCondition);
|
|
298
|
+
const cond = typeof ac.condition === 'string' ? ac.condition.trim() : '';
|
|
299
|
+
if (cond) aiCondition = { ...ac, condition: cond };
|
|
300
|
+
} else if (typeof src.aiFilter === 'string' && src.aiFilter.trim()) {
|
|
301
|
+
aiCondition = { condition: src.aiFilter.trim() };
|
|
302
|
+
}
|
|
303
|
+
const df = normalizeConditionsDataFilters(src.dataFilters);
|
|
304
|
+
const catalogIds = Array.isArray(src.dataFilters)
|
|
305
|
+
? src.dataFilters.map((x) => String(x).trim()).filter(Boolean)
|
|
306
|
+
: [];
|
|
307
|
+
/** @type {import('../types/graphContracts').GraphEntryConditions} */
|
|
308
|
+
const out = {};
|
|
309
|
+
if (narratives.length) out.narratives = narratives;
|
|
310
|
+
if (df && Array.isArray(df.rules) && df.rules.length > 0) out.dataFilters = df;
|
|
311
|
+
else if (catalogIds.length) out.dataFilters = catalogIds;
|
|
312
|
+
if (aiCondition) out.aiCondition = aiCondition;
|
|
313
|
+
if (src.jsonConditions && typeof src.jsonConditions === 'object' && !Array.isArray(src.jsonConditions)) {
|
|
314
|
+
out.jsonConditions = src.jsonConditions;
|
|
315
|
+
}
|
|
316
|
+
if (src.jsConditionFunction && typeof src.jsConditionFunction === 'object' && !Array.isArray(src.jsConditionFunction)) {
|
|
317
|
+
out.jsConditionFunction = src.jsConditionFunction;
|
|
318
|
+
}
|
|
319
|
+
return Object.keys(out).length ? out : undefined;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/** @param {unknown} o @param {string} k */
|
|
323
|
+
function hasOwn(o, k) {
|
|
324
|
+
return o != null && typeof o === 'object' && Object.prototype.hasOwnProperty.call(o, k);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Heuristic: paths that look like trace/debug go to debug bucket when splitting legacy `notableExecutionPaths`.
|
|
329
|
+
* @param {string} s
|
|
330
|
+
*/
|
|
331
|
+
export function isDebugResponsePath(s) {
|
|
332
|
+
const lower = String(s).toLowerCase();
|
|
333
|
+
return (
|
|
334
|
+
lower.includes('_trace') ||
|
|
335
|
+
lower.includes('.trace') ||
|
|
336
|
+
lower.includes('debug') ||
|
|
337
|
+
lower.endsWith('trace')
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* @param {string[]|undefined} paths
|
|
343
|
+
* @returns {{ primary: string[], debug: string[] }}
|
|
344
|
+
*/
|
|
345
|
+
export function splitNotableExecutionPaths(paths) {
|
|
346
|
+
const primary = [];
|
|
347
|
+
const debug = [];
|
|
348
|
+
if (!Array.isArray(paths)) return { primary, debug };
|
|
349
|
+
for (const p of paths) {
|
|
350
|
+
const s = String(p).trim();
|
|
351
|
+
if (!s) continue;
|
|
352
|
+
if (isDebugResponsePath(s)) debug.push(s);
|
|
353
|
+
else primary.push(s);
|
|
354
|
+
}
|
|
355
|
+
return { primary, debug };
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* @param {Record<string, unknown>|undefined} metadata `graph.metadata`
|
|
360
|
+
* @returns {{
|
|
361
|
+
* summary: string;
|
|
362
|
+
* inputs: import('../types/graphContracts').GraphEntryInputSpec[];
|
|
363
|
+
* exampleInput: import('../types/graphContracts').GraphEntryExampleInput;
|
|
364
|
+
* exampleInputs: import('../types/graphContracts').GraphEntryExampleInput[];
|
|
365
|
+
* requestExample: unknown;
|
|
366
|
+
* requestSchema: unknown;
|
|
367
|
+
* requiredRequestPaths: string[];
|
|
368
|
+
* conditions: import('../types/graphContracts').GraphEntryConditions | Record<string, never>;
|
|
369
|
+
* }}
|
|
370
|
+
*/
|
|
371
|
+
export function getEntryRequestViewState(metadata) {
|
|
372
|
+
const ge = metadata?.graphEntry;
|
|
373
|
+
if (!ge || typeof ge !== 'object') {
|
|
374
|
+
return {
|
|
375
|
+
summary: '',
|
|
376
|
+
inputs: [],
|
|
377
|
+
exampleInput: { format: 'json', value: '' },
|
|
378
|
+
exampleInputs: [],
|
|
379
|
+
requestExample: undefined,
|
|
380
|
+
requestSchema: undefined,
|
|
381
|
+
requiredRequestPaths: [],
|
|
382
|
+
conditions: {},
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
return {
|
|
386
|
+
summary: typeof ge.summary === 'string' ? ge.summary : '',
|
|
387
|
+
inputs: normalizeGraphEntryInputs(ge),
|
|
388
|
+
exampleInput: normalizeGraphEntryExampleInput(ge),
|
|
389
|
+
exampleInputs: normalizeGraphEntryExampleInputs(ge),
|
|
390
|
+
requestExample: ge.requestExample !== undefined ? ge.requestExample : ge.executionExample,
|
|
391
|
+
requestSchema: ge.requestSchema !== undefined ? ge.requestSchema : ge.executionSchema,
|
|
392
|
+
requiredRequestPaths: Array.isArray(ge.requiredExecutionPaths) ? [...ge.requiredExecutionPaths] : [],
|
|
393
|
+
conditions: normalizeGraphEntryConditions(ge.conditions) ?? {},
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/** @param {Record<string, unknown>|undefined} metadata `graph.metadata` @param {unknown} [rootResponse] canonical `graph.response` */
|
|
398
|
+
export function getResponseViewState(metadata, rootResponse) {
|
|
399
|
+
const studio = metadata?.graphsStudio && typeof metadata.graphsStudio === 'object' ? metadata.graphsStudio : {};
|
|
400
|
+
const gr =
|
|
401
|
+
metadata?.graphResponse && typeof metadata.graphResponse === 'object'
|
|
402
|
+
? metadata.graphResponse
|
|
403
|
+
: studio?.graphResponse && typeof studio.graphResponse === 'object'
|
|
404
|
+
? studio.graphResponse
|
|
405
|
+
: undefined;
|
|
406
|
+
const root = rootResponse && typeof rootResponse === 'object' && !Array.isArray(rootResponse) ? rootResponse : undefined;
|
|
407
|
+
if (!gr && !root) {
|
|
408
|
+
return {
|
|
409
|
+
summary: '',
|
|
410
|
+
responseExample: undefined,
|
|
411
|
+
outputExamples: [],
|
|
412
|
+
responseSchema: undefined,
|
|
413
|
+
responseMapping: undefined,
|
|
414
|
+
primaryResponsePaths: [],
|
|
415
|
+
debugResponsePaths: [],
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const summary = typeof gr?.summary === 'string' ? gr.summary : '';
|
|
420
|
+
const responseExample = gr?.responseExample !== undefined ? gr.responseExample : gr?.finalOutputExample;
|
|
421
|
+
const responseSchema = gr?.responseSchema !== undefined ? gr.responseSchema : gr?.finalOutputSchema;
|
|
422
|
+
const responseMapping = root !== undefined ? root : gr?.responseMapping !== undefined ? gr.responseMapping : undefined;
|
|
423
|
+
|
|
424
|
+
let primaryResponsePaths;
|
|
425
|
+
let debugResponsePaths;
|
|
426
|
+
|
|
427
|
+
if (gr && (hasOwn(gr, 'primaryResponsePaths') || hasOwn(gr, 'debugResponsePaths'))) {
|
|
428
|
+
primaryResponsePaths = Array.isArray(gr.primaryResponsePaths) ? [...gr.primaryResponsePaths] : [];
|
|
429
|
+
debugResponsePaths = Array.isArray(gr.debugResponsePaths) ? [...gr.debugResponsePaths] : [];
|
|
430
|
+
} else {
|
|
431
|
+
const split = splitNotableExecutionPaths(gr?.notableExecutionPaths);
|
|
432
|
+
primaryResponsePaths = split.primary;
|
|
433
|
+
debugResponsePaths = split.debug;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
return {
|
|
437
|
+
summary,
|
|
438
|
+
responseExample,
|
|
439
|
+
outputExamples: normalizeGraphResponseOutputExamples(gr),
|
|
440
|
+
responseSchema,
|
|
441
|
+
responseMapping,
|
|
442
|
+
primaryResponsePaths,
|
|
443
|
+
debugResponsePaths,
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Shallow patch with undefined = delete key.
|
|
449
|
+
* @param {Record<string, unknown>|undefined} prev
|
|
450
|
+
* @param {Record<string, unknown>} patch
|
|
451
|
+
*/
|
|
452
|
+
export function applyObjectPatch(prev, patch) {
|
|
453
|
+
const base = { ...(prev && typeof prev === 'object' ? prev : {}) };
|
|
454
|
+
for (const [k, v] of Object.entries(patch)) {
|
|
455
|
+
if (v === undefined) delete base[k];
|
|
456
|
+
else base[k] = v;
|
|
457
|
+
}
|
|
458
|
+
return base;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* @param {Record<string, unknown>|undefined} prevGe
|
|
463
|
+
* @param {Record<string, unknown>} patch
|
|
464
|
+
*/
|
|
465
|
+
export function applyGraphEntryPatch(prevGe, patch) {
|
|
466
|
+
return applyObjectPatch(prevGe, patch);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Keeps `notableExecutionPaths` aligned with primary + debug for older consumers.
|
|
471
|
+
* @param {Record<string, unknown>|undefined} prevGr
|
|
472
|
+
* @param {Record<string, unknown>} patch
|
|
473
|
+
*/
|
|
474
|
+
export function applyGraphResponsePatch(prevGr, patch) {
|
|
475
|
+
const base = applyObjectPatch(prevGr, patch);
|
|
476
|
+
if (hasOwn(patch, 'primaryResponsePaths') || hasOwn(patch, 'debugResponsePaths')) {
|
|
477
|
+
const p = Array.isArray(base.primaryResponsePaths) ? base.primaryResponsePaths : [];
|
|
478
|
+
const d = Array.isArray(base.debugResponsePaths) ? base.debugResponsePaths : [];
|
|
479
|
+
const merged = [...new Set([...p, ...d].map((x) => String(x).trim()).filter(Boolean))];
|
|
480
|
+
if (merged.length) base.notableExecutionPaths = merged;
|
|
481
|
+
else delete base.notableExecutionPaths;
|
|
482
|
+
}
|
|
483
|
+
return base;
|
|
484
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './informationFlowLayerDocument.js';
|
|
2
|
+
export { applyInformationFlowLayerToGraph as applyDataFlowLayerToGraph } from './informationFlowLayerDocument.js';
|
|
3
|
+
|
|
4
|
+
export {
|
|
5
|
+
getEntryRequestViewState,
|
|
6
|
+
getResponseViewState,
|
|
7
|
+
isDebugResponsePath,
|
|
8
|
+
} from './graphContractMetadata.js';
|