@contractspec/lib.example-shared-ui 6.0.17 → 6.0.19
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/.turbo/turbo-build.log +81 -81
- package/CHANGELOG.md +28 -0
- package/dist/EvolutionDashboard.js +1 -803
- package/dist/EvolutionSidebar.js +1 -531
- package/dist/LocalDataIndicator.js +1 -68
- package/dist/MarkdownView.js +1 -213
- package/dist/OverlayContextProvider.js +1 -202
- package/dist/PersonalizationInsights.js +1 -455
- package/dist/SaveToStudioButton.js +1 -79
- package/dist/SpecDrivenTemplateShell.js +1 -203
- package/dist/SpecEditorPanel.js +17 -364
- package/dist/TemplateShell.js +1 -195
- package/dist/browser/EvolutionDashboard.js +1 -803
- package/dist/browser/EvolutionSidebar.js +1 -531
- package/dist/browser/LocalDataIndicator.js +1 -68
- package/dist/browser/MarkdownView.js +1 -213
- package/dist/browser/OverlayContextProvider.js +1 -202
- package/dist/browser/PersonalizationInsights.js +1 -455
- package/dist/browser/SaveToStudioButton.js +1 -79
- package/dist/browser/SpecDrivenTemplateShell.js +1 -203
- package/dist/browser/SpecEditorPanel.js +17 -364
- package/dist/browser/TemplateShell.js +1 -195
- package/dist/browser/bundles/ExampleTemplateBundle.js +1 -85
- package/dist/browser/bundles/index.js +1 -85
- package/dist/browser/hooks/index.js +40 -1151
- package/dist/browser/hooks/useBehaviorTracking.js +1 -157
- package/dist/browser/hooks/useEvolution.js +1 -260
- package/dist/browser/hooks/useRegistryTemplates.js +1 -31
- package/dist/browser/hooks/useSpecContent.js +17 -224
- package/dist/browser/hooks/useWorkflowComposer.js +24 -483
- package/dist/browser/index.js +40 -3122
- package/dist/browser/lib/component-registry.js +1 -48
- package/dist/browser/lib/runtime-context.js +1 -21
- package/dist/browser/markdown/formatPresentationName.js +1 -9
- package/dist/browser/markdown/useMarkdownPresentation.js +1 -65
- package/dist/browser/utils/fetchPresentationData.js +1 -15
- package/dist/browser/utils/generateSpecFromTemplate.js +16 -62
- package/dist/browser/utils/index.js +16 -76
- package/dist/bundles/ExampleTemplateBundle.js +1 -85
- package/dist/bundles/index.js +1 -85
- package/dist/hooks/index.js +40 -1151
- package/dist/hooks/useBehaviorTracking.js +1 -157
- package/dist/hooks/useEvolution.js +1 -260
- package/dist/hooks/useRegistryTemplates.js +1 -31
- package/dist/hooks/useSpecContent.js +17 -224
- package/dist/hooks/useWorkflowComposer.js +24 -483
- package/dist/index.js +40 -3122
- package/dist/lib/component-registry.js +1 -48
- package/dist/lib/runtime-context.js +1 -21
- package/dist/markdown/formatPresentationName.js +1 -9
- package/dist/markdown/useMarkdownPresentation.js +1 -65
- package/dist/node/EvolutionDashboard.js +1 -803
- package/dist/node/EvolutionSidebar.js +1 -531
- package/dist/node/LocalDataIndicator.js +1 -68
- package/dist/node/MarkdownView.js +1 -213
- package/dist/node/OverlayContextProvider.js +1 -202
- package/dist/node/PersonalizationInsights.js +1 -455
- package/dist/node/SaveToStudioButton.js +1 -79
- package/dist/node/SpecDrivenTemplateShell.js +1 -203
- package/dist/node/SpecEditorPanel.js +17 -364
- package/dist/node/TemplateShell.js +1 -195
- package/dist/node/bundles/ExampleTemplateBundle.js +1 -85
- package/dist/node/bundles/index.js +1 -85
- package/dist/node/hooks/index.js +40 -1151
- package/dist/node/hooks/useBehaviorTracking.js +1 -157
- package/dist/node/hooks/useEvolution.js +1 -260
- package/dist/node/hooks/useRegistryTemplates.js +1 -31
- package/dist/node/hooks/useSpecContent.js +17 -224
- package/dist/node/hooks/useWorkflowComposer.js +24 -483
- package/dist/node/index.js +40 -3122
- package/dist/node/lib/component-registry.js +1 -48
- package/dist/node/lib/runtime-context.js +1 -21
- package/dist/node/markdown/formatPresentationName.js +1 -9
- package/dist/node/markdown/useMarkdownPresentation.js +1 -65
- package/dist/node/utils/fetchPresentationData.js +1 -15
- package/dist/node/utils/generateSpecFromTemplate.js +16 -62
- package/dist/node/utils/index.js +16 -76
- package/dist/utils/fetchPresentationData.js +1 -15
- package/dist/utils/generateSpecFromTemplate.js +16 -62
- package/dist/utils/index.js +16 -76
- package/package.json +13 -13
- package/src/lib/component-registry.tsx +2 -1
- package/src/lib/runtime-context.tsx +1 -1
- package/src/lib/singletons.test.ts +19 -22
|
@@ -1,460 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
"use client";
|
|
4
|
-
var EVOLUTION_STORAGE_KEY = "contractspec-evolution-data";
|
|
5
|
-
function useEvolution(templateId) {
|
|
6
|
-
const [usageStats, setUsageStats] = useState([]);
|
|
7
|
-
const [anomalies, setAnomalies] = useState([]);
|
|
8
|
-
const [suggestions, setSuggestions] = useState([]);
|
|
9
|
-
const [hints, setHints] = useState([]);
|
|
10
|
-
const [loading, setLoading] = useState(false);
|
|
11
|
-
const metricsRef = useRef([]);
|
|
12
|
-
const [operationCount, setOperationCount] = useState(0);
|
|
13
|
-
useEffect(() => {
|
|
14
|
-
try {
|
|
15
|
-
const stored = localStorage.getItem(`${EVOLUTION_STORAGE_KEY}-${templateId}`);
|
|
16
|
-
if (stored) {
|
|
17
|
-
const data = JSON.parse(stored);
|
|
18
|
-
setSuggestions(data.suggestions.map((s) => ({
|
|
19
|
-
...s,
|
|
20
|
-
createdAt: new Date(s.createdAt)
|
|
21
|
-
})));
|
|
22
|
-
}
|
|
23
|
-
} catch {}
|
|
24
|
-
}, [templateId]);
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
try {
|
|
27
|
-
localStorage.setItem(`${EVOLUTION_STORAGE_KEY}-${templateId}`, JSON.stringify({ suggestions }));
|
|
28
|
-
} catch {}
|
|
29
|
-
}, [suggestions, templateId]);
|
|
30
|
-
const trackOperation = useCallback((operationName, durationMs, success, errorCode) => {
|
|
31
|
-
const sample = {
|
|
32
|
-
operation: {
|
|
33
|
-
name: operationName,
|
|
34
|
-
version: "1.0.0",
|
|
35
|
-
tenantId: "sandbox"
|
|
36
|
-
},
|
|
37
|
-
durationMs,
|
|
38
|
-
success,
|
|
39
|
-
timestamp: new Date,
|
|
40
|
-
errorCode
|
|
41
|
-
};
|
|
42
|
-
metricsRef.current.push(sample);
|
|
43
|
-
setOperationCount((prev) => prev + 1);
|
|
44
|
-
}, []);
|
|
45
|
-
const analyzeUsage = useCallback(() => {
|
|
46
|
-
const samples = metricsRef.current;
|
|
47
|
-
if (samples.length < 5)
|
|
48
|
-
return;
|
|
49
|
-
const groups = new Map;
|
|
50
|
-
for (const sample of samples) {
|
|
51
|
-
const key = `${sample.operation.name}.v${sample.operation.version}`;
|
|
52
|
-
const arr = groups.get(key) ?? [];
|
|
53
|
-
arr.push(sample);
|
|
54
|
-
groups.set(key, arr);
|
|
55
|
-
}
|
|
56
|
-
const stats = [];
|
|
57
|
-
const detectedAnomalies = [];
|
|
58
|
-
groups.forEach((opSamples) => {
|
|
59
|
-
if (opSamples.length < 3)
|
|
60
|
-
return;
|
|
61
|
-
const durations = opSamples.map((s) => s.durationMs).sort((a, b) => a - b);
|
|
62
|
-
const errors = opSamples.filter((s) => !s.success);
|
|
63
|
-
const totalCalls = opSamples.length;
|
|
64
|
-
const errorRate = errors.length / totalCalls;
|
|
65
|
-
const averageLatencyMs = durations.reduce((sum, value) => sum + value, 0) / totalCalls;
|
|
66
|
-
const timestamps = opSamples.map((s) => s.timestamp.getTime());
|
|
67
|
-
const firstSample = opSamples[0];
|
|
68
|
-
if (!firstSample)
|
|
69
|
-
return;
|
|
70
|
-
const stat = {
|
|
71
|
-
operation: firstSample.operation,
|
|
72
|
-
totalCalls,
|
|
73
|
-
successRate: 1 - errorRate,
|
|
74
|
-
errorRate,
|
|
75
|
-
averageLatencyMs,
|
|
76
|
-
p95LatencyMs: percentile(durations, 0.95),
|
|
77
|
-
p99LatencyMs: percentile(durations, 0.99),
|
|
78
|
-
maxLatencyMs: Math.max(...durations),
|
|
79
|
-
lastSeenAt: new Date(Math.max(...timestamps)),
|
|
80
|
-
windowStart: new Date(Math.min(...timestamps)),
|
|
81
|
-
windowEnd: new Date(Math.max(...timestamps)),
|
|
82
|
-
topErrors: errors.reduce((acc, s) => {
|
|
83
|
-
if (s.errorCode) {
|
|
84
|
-
acc[s.errorCode] = (acc[s.errorCode] ?? 0) + 1;
|
|
85
|
-
}
|
|
86
|
-
return acc;
|
|
87
|
-
}, {})
|
|
88
|
-
};
|
|
89
|
-
stats.push(stat);
|
|
90
|
-
if (errorRate > 0.1) {
|
|
91
|
-
detectedAnomalies.push({
|
|
92
|
-
operation: stat.operation,
|
|
93
|
-
severity: errorRate > 0.3 ? "high" : errorRate > 0.2 ? "medium" : "low",
|
|
94
|
-
metric: "error-rate",
|
|
95
|
-
description: `Error rate ${(errorRate * 100).toFixed(1)}% exceeds threshold`,
|
|
96
|
-
detectedAt: new Date,
|
|
97
|
-
threshold: 0.1,
|
|
98
|
-
observedValue: errorRate
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
if (stat.p99LatencyMs > 500) {
|
|
102
|
-
detectedAnomalies.push({
|
|
103
|
-
operation: stat.operation,
|
|
104
|
-
severity: stat.p99LatencyMs > 1000 ? "high" : stat.p99LatencyMs > 750 ? "medium" : "low",
|
|
105
|
-
metric: "latency",
|
|
106
|
-
description: `P99 latency ${stat.p99LatencyMs.toFixed(0)}ms exceeds threshold`,
|
|
107
|
-
detectedAt: new Date,
|
|
108
|
-
threshold: 500,
|
|
109
|
-
observedValue: stat.p99LatencyMs
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
setUsageStats(stats);
|
|
114
|
-
setAnomalies(detectedAnomalies);
|
|
115
|
-
const newHints = detectedAnomalies.map((anomaly) => ({
|
|
116
|
-
operation: anomaly.operation,
|
|
117
|
-
category: anomaly.metric === "latency" ? "performance" : "error-handling",
|
|
118
|
-
summary: anomaly.metric === "latency" ? "Latency regression detected" : "Error spike detected",
|
|
119
|
-
justification: anomaly.description,
|
|
120
|
-
recommendedActions: anomaly.metric === "latency" ? [
|
|
121
|
-
"Add caching layer",
|
|
122
|
-
"Optimize database queries",
|
|
123
|
-
"Consider pagination"
|
|
124
|
-
] : [
|
|
125
|
-
"Add retry logic",
|
|
126
|
-
"Improve error handling",
|
|
127
|
-
"Add circuit breaker"
|
|
128
|
-
]
|
|
129
|
-
}));
|
|
130
|
-
setHints(newHints);
|
|
131
|
-
}, []);
|
|
132
|
-
const generateSuggestions = useCallback(async () => {
|
|
133
|
-
if (anomalies.length === 0)
|
|
134
|
-
return;
|
|
135
|
-
setLoading(true);
|
|
136
|
-
await new Promise((resolve) => setTimeout(resolve, 800));
|
|
137
|
-
const newSuggestions = anomalies.map((anomaly) => ({
|
|
138
|
-
id: `suggestion-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
|
|
139
|
-
intent: {
|
|
140
|
-
id: `intent-${anomaly.operation.name}`,
|
|
141
|
-
type: anomaly.metric === "latency" ? "latency-regression" : anomaly.metric === "error-rate" ? "error-spike" : "throughput-drop",
|
|
142
|
-
description: anomaly.description,
|
|
143
|
-
operation: anomaly.operation,
|
|
144
|
-
confidence: {
|
|
145
|
-
score: anomaly.severity === "high" ? 0.9 : anomaly.severity === "medium" ? 0.7 : 0.5,
|
|
146
|
-
sampleSize: usageStats.find((s) => s.operation.name === anomaly.operation.name)?.totalCalls ?? 0
|
|
147
|
-
}
|
|
148
|
-
},
|
|
149
|
-
target: anomaly.operation,
|
|
150
|
-
proposal: {
|
|
151
|
-
summary: generateSuggestionSummary(anomaly),
|
|
152
|
-
rationale: generateSuggestionRationale(anomaly),
|
|
153
|
-
changeType: anomaly.metric === "error-rate" ? "policy-update" : "revision",
|
|
154
|
-
recommendedActions: generateRecommendedActions(anomaly)
|
|
155
|
-
},
|
|
156
|
-
confidence: anomaly.severity === "high" ? 0.85 : anomaly.severity === "medium" ? 0.7 : 0.55,
|
|
157
|
-
createdAt: new Date,
|
|
158
|
-
createdBy: "ai-evolution-agent",
|
|
159
|
-
status: "pending",
|
|
160
|
-
priority: anomaly.severity
|
|
161
|
-
}));
|
|
162
|
-
setSuggestions((prev) => [...prev, ...newSuggestions]);
|
|
163
|
-
setLoading(false);
|
|
164
|
-
}, [anomalies, usageStats]);
|
|
165
|
-
const approveSuggestion = useCallback((id, _notes) => {
|
|
166
|
-
setSuggestions((prev) => prev.map((s) => s.id === id ? { ...s, status: "approved" } : s));
|
|
167
|
-
}, []);
|
|
168
|
-
const rejectSuggestion = useCallback((id, _notes) => {
|
|
169
|
-
setSuggestions((prev) => prev.map((s) => s.id === id ? { ...s, status: "rejected" } : s));
|
|
170
|
-
}, []);
|
|
171
|
-
const clear = useCallback(() => {
|
|
172
|
-
metricsRef.current = [];
|
|
173
|
-
setOperationCount(0);
|
|
174
|
-
setUsageStats([]);
|
|
175
|
-
setAnomalies([]);
|
|
176
|
-
setSuggestions([]);
|
|
177
|
-
setHints([]);
|
|
178
|
-
localStorage.removeItem(`${EVOLUTION_STORAGE_KEY}-${templateId}`);
|
|
179
|
-
}, [templateId]);
|
|
180
|
-
return useMemo(() => ({
|
|
181
|
-
usageStats,
|
|
182
|
-
anomalies,
|
|
183
|
-
suggestions,
|
|
184
|
-
hints,
|
|
185
|
-
loading,
|
|
186
|
-
trackOperation,
|
|
187
|
-
analyzeUsage,
|
|
188
|
-
generateSuggestions,
|
|
189
|
-
approveSuggestion,
|
|
190
|
-
rejectSuggestion,
|
|
191
|
-
clear,
|
|
192
|
-
operationCount
|
|
193
|
-
}), [
|
|
194
|
-
usageStats,
|
|
195
|
-
anomalies,
|
|
196
|
-
suggestions,
|
|
197
|
-
hints,
|
|
198
|
-
loading,
|
|
199
|
-
trackOperation,
|
|
200
|
-
analyzeUsage,
|
|
201
|
-
generateSuggestions,
|
|
202
|
-
approveSuggestion,
|
|
203
|
-
rejectSuggestion,
|
|
204
|
-
clear,
|
|
205
|
-
operationCount
|
|
206
|
-
]);
|
|
207
|
-
}
|
|
208
|
-
function percentile(values, p) {
|
|
209
|
-
if (!values.length)
|
|
210
|
-
return 0;
|
|
211
|
-
if (values.length === 1)
|
|
212
|
-
return values[0] ?? 0;
|
|
213
|
-
const idx = Math.min(values.length - 1, Math.floor(p * values.length));
|
|
214
|
-
return values[idx] ?? 0;
|
|
215
|
-
}
|
|
216
|
-
function generateSuggestionSummary(anomaly) {
|
|
217
|
-
if (anomaly.metric === "latency") {
|
|
218
|
-
return `Add caching and pagination to ${anomaly.operation.name} to reduce latency`;
|
|
219
|
-
}
|
|
220
|
-
if (anomaly.metric === "error-rate") {
|
|
221
|
-
return `Add retry policy and circuit breaker to ${anomaly.operation.name}`;
|
|
222
|
-
}
|
|
223
|
-
return `Optimize ${anomaly.operation.name} for improved throughput`;
|
|
224
|
-
}
|
|
225
|
-
function generateSuggestionRationale(anomaly) {
|
|
226
|
-
if (anomaly.metric === "latency") {
|
|
227
|
-
return `The operation ${anomaly.operation.name} is experiencing P99 latency of ${anomaly.observedValue?.toFixed(0)}ms, which is above the recommended threshold of ${anomaly.threshold}ms. This can impact user experience and downstream operations.`;
|
|
228
|
-
}
|
|
229
|
-
if (anomaly.metric === "error-rate") {
|
|
230
|
-
return `The error rate for ${anomaly.operation.name} is ${((anomaly.observedValue ?? 0) * 100).toFixed(1)}%, indicating potential issues with input validation, external dependencies, or resource limits.`;
|
|
231
|
-
}
|
|
232
|
-
return `Throughput for ${anomaly.operation.name} has dropped significantly, suggesting potential bottlenecks or reduced demand that should be investigated.`;
|
|
233
|
-
}
|
|
234
|
-
function generateRecommendedActions(anomaly) {
|
|
235
|
-
if (anomaly.metric === "latency") {
|
|
236
|
-
return [
|
|
237
|
-
"Add response caching for frequently accessed data",
|
|
238
|
-
"Implement pagination for large result sets",
|
|
239
|
-
"Optimize database queries with proper indexing",
|
|
240
|
-
"Consider adding a GraphQL DataLoader for batching"
|
|
241
|
-
];
|
|
242
|
-
}
|
|
243
|
-
if (anomaly.metric === "error-rate") {
|
|
244
|
-
return [
|
|
245
|
-
"Add input validation at the contract level",
|
|
246
|
-
"Implement retry policy with exponential backoff",
|
|
247
|
-
"Add circuit breaker for external dependencies",
|
|
248
|
-
"Enhance error logging for better debugging"
|
|
249
|
-
];
|
|
250
|
-
}
|
|
251
|
-
return [
|
|
252
|
-
"Review resource allocation and scaling policies",
|
|
253
|
-
"Check for upstream routing or load balancer issues",
|
|
254
|
-
"Validate feature flag configurations",
|
|
255
|
-
"Monitor dependency health metrics"
|
|
256
|
-
];
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// src/lib/runtime-context.tsx
|
|
260
|
-
import { createContext, useContext } from "react";
|
|
261
|
-
"use client";
|
|
262
|
-
var TEMPLATE_RUNTIME_CONTEXT_KEY = Symbol.for("@contractspec/lib.example-shared-ui/template-runtime-context");
|
|
263
|
-
function getTemplateRuntimeContextSingleton() {
|
|
264
|
-
const store = globalThis;
|
|
265
|
-
store[TEMPLATE_RUNTIME_CONTEXT_KEY] ??= createContext(null);
|
|
266
|
-
return store[TEMPLATE_RUNTIME_CONTEXT_KEY];
|
|
267
|
-
}
|
|
268
|
-
var TemplateRuntimeContext = getTemplateRuntimeContextSingleton();
|
|
269
|
-
function useTemplateRuntime() {
|
|
270
|
-
const context = useContext(TemplateRuntimeContext);
|
|
271
|
-
if (!context) {
|
|
272
|
-
throw new Error("useTemplateRuntime must be used within a TemplateRuntimeProvider");
|
|
273
|
-
}
|
|
274
|
-
return context;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// src/hooks/useBehaviorTracking.ts
|
|
278
|
-
import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
|
|
279
|
-
"use client";
|
|
280
|
-
var BEHAVIOR_STORAGE_KEY = "contractspec-behavior-data";
|
|
281
|
-
var ALL_FEATURES = [
|
|
282
|
-
"playground",
|
|
283
|
-
"specs",
|
|
284
|
-
"builder",
|
|
285
|
-
"markdown",
|
|
286
|
-
"evolution",
|
|
287
|
-
"canvas_add",
|
|
288
|
-
"canvas_delete",
|
|
289
|
-
"spec_save",
|
|
290
|
-
"spec_validate",
|
|
291
|
-
"ai_suggestions"
|
|
292
|
-
];
|
|
293
|
-
function useBehaviorTracking(templateId) {
|
|
294
|
-
const [events, setEvents] = useState2([]);
|
|
295
|
-
const sessionStartRef = useRef2(new Date);
|
|
296
|
-
const [eventCount, setEventCount] = useState2(0);
|
|
297
|
-
useEffect2(() => {
|
|
298
|
-
try {
|
|
299
|
-
const stored = localStorage.getItem(BEHAVIOR_STORAGE_KEY);
|
|
300
|
-
if (stored) {
|
|
301
|
-
const data = JSON.parse(stored);
|
|
302
|
-
setEvents(data.events.map((e) => ({
|
|
303
|
-
...e,
|
|
304
|
-
timestamp: new Date(e.timestamp)
|
|
305
|
-
})));
|
|
306
|
-
sessionStartRef.current = new Date(data.sessionStart);
|
|
307
|
-
}
|
|
308
|
-
} catch {}
|
|
309
|
-
}, []);
|
|
310
|
-
useEffect2(() => {
|
|
311
|
-
if (events.length > 0) {
|
|
312
|
-
try {
|
|
313
|
-
localStorage.setItem(BEHAVIOR_STORAGE_KEY, JSON.stringify({
|
|
314
|
-
events: events.map((e) => ({
|
|
315
|
-
...e,
|
|
316
|
-
timestamp: e.timestamp.toISOString()
|
|
317
|
-
})),
|
|
318
|
-
sessionStart: sessionStartRef.current.toISOString()
|
|
319
|
-
}));
|
|
320
|
-
} catch {}
|
|
321
|
-
}
|
|
322
|
-
}, [events]);
|
|
323
|
-
const trackEvent = useCallback2((type, metadata) => {
|
|
324
|
-
const event = {
|
|
325
|
-
type,
|
|
326
|
-
timestamp: new Date,
|
|
327
|
-
templateId,
|
|
328
|
-
metadata
|
|
329
|
-
};
|
|
330
|
-
setEvents((prev) => [...prev, event]);
|
|
331
|
-
setEventCount((prev) => prev + 1);
|
|
332
|
-
}, [templateId]);
|
|
333
|
-
const getEventsByType = useCallback2((type) => {
|
|
334
|
-
return events.filter((e) => e.type === type);
|
|
335
|
-
}, [events]);
|
|
336
|
-
const getSummary = useCallback2(() => {
|
|
337
|
-
const now = new Date;
|
|
338
|
-
const sessionDuration = now.getTime() - sessionStartRef.current.getTime();
|
|
339
|
-
const templateCounts = new Map;
|
|
340
|
-
for (const event of events) {
|
|
341
|
-
const count = templateCounts.get(event.templateId) ?? 0;
|
|
342
|
-
templateCounts.set(event.templateId, count + 1);
|
|
343
|
-
}
|
|
344
|
-
const mostUsedTemplates = Array.from(templateCounts.entries()).map(([templateId2, count]) => ({ templateId: templateId2, count })).sort((a, b) => b.count - a.count).slice(0, 3);
|
|
345
|
-
const modeCounts = new Map;
|
|
346
|
-
for (const event of events) {
|
|
347
|
-
if (event.type === "mode_change" && event.metadata?.mode) {
|
|
348
|
-
const mode = event.metadata.mode;
|
|
349
|
-
const count = modeCounts.get(mode) ?? 0;
|
|
350
|
-
modeCounts.set(mode, count + 1);
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
const mostUsedModes = Array.from(modeCounts.entries()).map(([mode, count]) => ({ mode, count })).sort((a, b) => b.count - a.count);
|
|
354
|
-
const featuresUsed = new Set;
|
|
355
|
-
for (const event of events) {
|
|
356
|
-
if (event.type === "mode_change" && event.metadata?.mode) {
|
|
357
|
-
featuresUsed.add(event.metadata.mode);
|
|
358
|
-
}
|
|
359
|
-
if (event.type === "feature_usage" && event.metadata?.feature) {
|
|
360
|
-
featuresUsed.add(event.metadata.feature);
|
|
361
|
-
}
|
|
362
|
-
if (event.type === "canvas_interaction") {
|
|
363
|
-
const action = event.metadata?.action;
|
|
364
|
-
if (action === "add")
|
|
365
|
-
featuresUsed.add("canvas_add");
|
|
366
|
-
if (action === "delete")
|
|
367
|
-
featuresUsed.add("canvas_delete");
|
|
368
|
-
}
|
|
369
|
-
if (event.type === "spec_edit") {
|
|
370
|
-
const action = event.metadata?.action;
|
|
371
|
-
if (action === "save")
|
|
372
|
-
featuresUsed.add("spec_save");
|
|
373
|
-
if (action === "validate")
|
|
374
|
-
featuresUsed.add("spec_validate");
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
const unusedFeatures = ALL_FEATURES.filter((f) => !featuresUsed.has(f));
|
|
378
|
-
const errorCount = events.filter((e) => e.type === "error").length;
|
|
379
|
-
const recommendations = generateRecommendations(Array.from(featuresUsed), unusedFeatures, mostUsedModes, events.length);
|
|
380
|
-
return {
|
|
381
|
-
totalEvents: events.length,
|
|
382
|
-
sessionDuration,
|
|
383
|
-
mostUsedTemplates,
|
|
384
|
-
mostUsedModes,
|
|
385
|
-
featuresUsed: Array.from(featuresUsed),
|
|
386
|
-
unusedFeatures,
|
|
387
|
-
errorCount,
|
|
388
|
-
recommendations
|
|
389
|
-
};
|
|
390
|
-
}, [events]);
|
|
391
|
-
const clear = useCallback2(() => {
|
|
392
|
-
setEvents([]);
|
|
393
|
-
setEventCount(0);
|
|
394
|
-
sessionStartRef.current = new Date;
|
|
395
|
-
localStorage.removeItem(BEHAVIOR_STORAGE_KEY);
|
|
396
|
-
}, []);
|
|
397
|
-
return useMemo2(() => ({
|
|
398
|
-
trackEvent,
|
|
399
|
-
getSummary,
|
|
400
|
-
getEventsByType,
|
|
401
|
-
eventCount,
|
|
402
|
-
sessionStart: sessionStartRef.current,
|
|
403
|
-
clear
|
|
404
|
-
}), [trackEvent, getSummary, getEventsByType, eventCount, clear]);
|
|
405
|
-
}
|
|
406
|
-
function generateRecommendations(featuresUsed, unusedFeatures, mostUsedModes, totalEvents) {
|
|
407
|
-
const recommendations = [];
|
|
408
|
-
if (unusedFeatures.includes("evolution")) {
|
|
409
|
-
recommendations.push("Try the AI Evolution mode to get automated improvement suggestions");
|
|
410
|
-
}
|
|
411
|
-
if (unusedFeatures.includes("markdown")) {
|
|
412
|
-
recommendations.push("Use Markdown preview to see documentation for your specs");
|
|
413
|
-
}
|
|
414
|
-
if (unusedFeatures.includes("builder")) {
|
|
415
|
-
recommendations.push("Explore the Visual Builder to design your UI components");
|
|
416
|
-
}
|
|
417
|
-
if (!featuresUsed.includes("spec_validate") && featuresUsed.includes("specs")) {
|
|
418
|
-
recommendations.push("Don't forget to validate your specs before saving");
|
|
419
|
-
}
|
|
420
|
-
if (featuresUsed.includes("evolution") && !featuresUsed.includes("ai_suggestions")) {
|
|
421
|
-
recommendations.push("Generate AI suggestions to get actionable improvement recommendations");
|
|
422
|
-
}
|
|
423
|
-
if (totalEvents > 50) {
|
|
424
|
-
recommendations.push("Great engagement! Consider saving your work regularly");
|
|
425
|
-
}
|
|
426
|
-
if (mostUsedModes.length === 1) {
|
|
427
|
-
recommendations.push("Try different modes to explore all sandbox capabilities");
|
|
428
|
-
}
|
|
429
|
-
return recommendations;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
// src/utils/generateSpecFromTemplate.ts
|
|
433
|
-
function generateSpecFromTemplate(template) {
|
|
434
|
-
const templateId = template?.id ?? "unknown";
|
|
435
|
-
if (!template) {
|
|
436
|
-
return generateDefaultSpec(templateId);
|
|
437
|
-
}
|
|
438
|
-
switch (templateId) {
|
|
439
|
-
case "crm-pipeline":
|
|
440
|
-
return generateCrmPipelineSpec(template.schema.contracts);
|
|
441
|
-
case "saas-boilerplate":
|
|
442
|
-
return generateSaasBoilerplateSpec(template.schema.contracts);
|
|
443
|
-
case "agent-console":
|
|
444
|
-
return generateAgentConsoleSpec(template.schema.contracts);
|
|
445
|
-
case "todos-app":
|
|
446
|
-
return generateTodosSpec(template.schema.contracts);
|
|
447
|
-
case "messaging-app":
|
|
448
|
-
return generateMessagingSpec(template.schema.contracts);
|
|
449
|
-
case "recipe-app-i18n":
|
|
450
|
-
return generateRecipeSpec(template.schema.contracts);
|
|
451
|
-
default:
|
|
452
|
-
return generateDefaultSpec(templateId);
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
function generateCrmPipelineSpec(contracts) {
|
|
456
|
-
return `// CRM Pipeline Specs
|
|
457
|
-
// Contracts: ${contracts.join(", ")}
|
|
1
|
+
import{useCallback as k,useEffect as l,useMemo as $q,useRef as Jq,useState as x}from"react";var c="contractspec-evolution-data";function bq(q){let[Z,X]=x([]),[z,D]=x([]),[H,B]=x([]),[_,T]=x([]),[L,j]=x(!1),G=Jq([]),[F,W]=x(0);l(()=>{try{let $=localStorage.getItem(`${c}-${q}`);if($){let J=JSON.parse($);B(J.suggestions.map((P)=>({...P,createdAt:new Date(P.createdAt)})))}}catch{}},[q]),l(()=>{try{localStorage.setItem(`${c}-${q}`,JSON.stringify({suggestions:H}))}catch{}},[H,q]);let O=k(($,J,P,Y)=>{let E={operation:{name:$,version:"1.0.0",tenantId:"sandbox"},durationMs:J,success:P,timestamp:new Date,errorCode:Y};G.current.push(E),W((V)=>V+1)},[]),A=k(()=>{let $=G.current;if($.length<5)return;let J=new Map;for(let V of $){let U=`${V.operation.name}.v${V.operation.version}`,I=J.get(U)??[];I.push(V),J.set(U,I)}let P=[],Y=[];J.forEach((V)=>{if(V.length<3)return;let U=V.map((M)=>M.durationMs).sort((M,w)=>M-w),I=V.filter((M)=>!M.success),i=V.length,b=I.length/i,qq=U.reduce((M,w)=>M+w,0)/i,u=V.map((M)=>M.timestamp.getTime()),m=V[0];if(!m)return;let R={operation:m.operation,totalCalls:i,successRate:1-b,errorRate:b,averageLatencyMs:qq,p95LatencyMs:n(U,0.95),p99LatencyMs:n(U,0.99),maxLatencyMs:Math.max(...U),lastSeenAt:new Date(Math.max(...u)),windowStart:new Date(Math.min(...u)),windowEnd:new Date(Math.max(...u)),topErrors:I.reduce((M,w)=>{if(w.errorCode)M[w.errorCode]=(M[w.errorCode]??0)+1;return M},{})};if(P.push(R),b>0.1)Y.push({operation:R.operation,severity:b>0.3?"high":b>0.2?"medium":"low",metric:"error-rate",description:`Error rate ${(b*100).toFixed(1)}% exceeds threshold`,detectedAt:new Date,threshold:0.1,observedValue:b});if(R.p99LatencyMs>500)Y.push({operation:R.operation,severity:R.p99LatencyMs>1000?"high":R.p99LatencyMs>750?"medium":"low",metric:"latency",description:`P99 latency ${R.p99LatencyMs.toFixed(0)}ms exceeds threshold`,detectedAt:new Date,threshold:500,observedValue:R.p99LatencyMs})}),X(P),D(Y);let E=Y.map((V)=>({operation:V.operation,category:V.metric==="latency"?"performance":"error-handling",summary:V.metric==="latency"?"Latency regression detected":"Error spike detected",justification:V.description,recommendedActions:V.metric==="latency"?["Add caching layer","Optimize database queries","Consider pagination"]:["Add retry logic","Improve error handling","Add circuit breaker"]}));T(E)},[]),h=k(async()=>{if(z.length===0)return;j(!0),await new Promise((J)=>setTimeout(J,800));let $=z.map((J)=>({id:`suggestion-${Date.now()}-${Math.random().toString(36).slice(2,9)}`,intent:{id:`intent-${J.operation.name}`,type:J.metric==="latency"?"latency-regression":J.metric==="error-rate"?"error-spike":"throughput-drop",description:J.description,operation:J.operation,confidence:{score:J.severity==="high"?0.9:J.severity==="medium"?0.7:0.5,sampleSize:Z.find((P)=>P.operation.name===J.operation.name)?.totalCalls??0}},target:J.operation,proposal:{summary:Zq(J),rationale:Qq(J),changeType:J.metric==="error-rate"?"policy-update":"revision",recommendedActions:Xq(J)},confidence:J.severity==="high"?0.85:J.severity==="medium"?0.7:0.55,createdAt:new Date,createdBy:"ai-evolution-agent",status:"pending",priority:J.severity}));B((J)=>[...J,...$]),j(!1)},[z,Z]),Q=k(($,J)=>{B((P)=>P.map((Y)=>Y.id===$?{...Y,status:"approved"}:Y))},[]),K=k(($,J)=>{B((P)=>P.map((Y)=>Y.id===$?{...Y,status:"rejected"}:Y))},[]),N=k(()=>{G.current=[],W(0),X([]),D([]),B([]),T([]),localStorage.removeItem(`${c}-${q}`)},[q]);return $q(()=>({usageStats:Z,anomalies:z,suggestions:H,hints:_,loading:L,trackOperation:O,analyzeUsage:A,generateSuggestions:h,approveSuggestion:Q,rejectSuggestion:K,clear:N,operationCount:F}),[Z,z,H,_,L,O,A,h,Q,K,N,F])}function n(q,Z){if(!q.length)return 0;if(q.length===1)return q[0]??0;let X=Math.min(q.length-1,Math.floor(Z*q.length));return q[X]??0}function Zq(q){if(q.metric==="latency")return`Add caching and pagination to ${q.operation.name} to reduce latency`;if(q.metric==="error-rate")return`Add retry policy and circuit breaker to ${q.operation.name}`;return`Optimize ${q.operation.name} for improved throughput`}function Qq(q){if(q.metric==="latency")return`The operation ${q.operation.name} is experiencing P99 latency of ${q.observedValue?.toFixed(0)}ms, which is above the recommended threshold of ${q.threshold}ms. This can impact user experience and downstream operations.`;if(q.metric==="error-rate")return`The error rate for ${q.operation.name} is ${((q.observedValue??0)*100).toFixed(1)}%, indicating potential issues with input validation, external dependencies, or resource limits.`;return`Throughput for ${q.operation.name} has dropped significantly, suggesting potential bottlenecks or reduced demand that should be investigated.`}function Xq(q){if(q.metric==="latency")return["Add response caching for frequently accessed data","Implement pagination for large result sets","Optimize database queries with proper indexing","Consider adding a GraphQL DataLoader for batching"];if(q.metric==="error-rate")return["Add input validation at the contract level","Implement retry policy with exponential backoff","Add circuit breaker for external dependencies","Enhance error logging for better debugging"];return["Review resource allocation and scaling policies","Check for upstream routing or load balancer issues","Validate feature flag configurations","Monitor dependency health metrics"]}import{createContext as zq,useContext as hq}from"react";var o=Symbol.for("@contractspec/lib.example-shared-ui/template-runtime-context");function Dq(){let q=globalThis;return q[o]??=zq(null),q[o]}var Hq=Dq();function s(){let q=hq(Hq);if(!q)throw Error("useTemplateRuntime must be used within a TemplateRuntimeProvider");return q}import{useCallback as f,useEffect as a,useMemo as jq,useRef as Pq,useState as t}from"react";var d="contractspec-behavior-data",Gq=["playground","specs","builder","markdown","evolution","canvas_add","canvas_delete","spec_save","spec_validate","ai_suggestions"];function Iq(q){let[Z,X]=t([]),z=Pq(new Date),[D,H]=t(0);a(()=>{try{let j=localStorage.getItem(d);if(j){let G=JSON.parse(j);X(G.events.map((F)=>({...F,timestamp:new Date(F.timestamp)}))),z.current=new Date(G.sessionStart)}}catch{}},[]),a(()=>{if(Z.length>0)try{localStorage.setItem(d,JSON.stringify({events:Z.map((j)=>({...j,timestamp:j.timestamp.toISOString()})),sessionStart:z.current.toISOString()}))}catch{}},[Z]);let B=f((j,G)=>{let F={type:j,timestamp:new Date,templateId:q,metadata:G};X((W)=>[...W,F]),H((W)=>W+1)},[q]),_=f((j)=>{return Z.filter((G)=>G.type===j)},[Z]),T=f(()=>{let G=new Date().getTime()-z.current.getTime(),F=new Map;for(let $ of Z){let J=F.get($.templateId)??0;F.set($.templateId,J+1)}let W=Array.from(F.entries()).map(([$,J])=>({templateId:$,count:J})).sort(($,J)=>J.count-$.count).slice(0,3),O=new Map;for(let $ of Z)if($.type==="mode_change"&&$.metadata?.mode){let J=$.metadata.mode,P=O.get(J)??0;O.set(J,P+1)}let A=Array.from(O.entries()).map(([$,J])=>({mode:$,count:J})).sort(($,J)=>J.count-$.count),h=new Set;for(let $ of Z){if($.type==="mode_change"&&$.metadata?.mode)h.add($.metadata.mode);if($.type==="feature_usage"&&$.metadata?.feature)h.add($.metadata.feature);if($.type==="canvas_interaction"){let J=$.metadata?.action;if(J==="add")h.add("canvas_add");if(J==="delete")h.add("canvas_delete")}if($.type==="spec_edit"){let J=$.metadata?.action;if(J==="save")h.add("spec_save");if(J==="validate")h.add("spec_validate")}}let Q=Gq.filter(($)=>!h.has($)),K=Z.filter(($)=>$.type==="error").length,N=Vq(Array.from(h),Q,A,Z.length);return{totalEvents:Z.length,sessionDuration:G,mostUsedTemplates:W,mostUsedModes:A,featuresUsed:Array.from(h),unusedFeatures:Q,errorCount:K,recommendations:N}},[Z]),L=f(()=>{X([]),H(0),z.current=new Date,localStorage.removeItem(d)},[]);return jq(()=>({trackEvent:B,getSummary:T,getEventsByType:_,eventCount:D,sessionStart:z.current,clear:L}),[B,T,_,D,L])}function Vq(q,Z,X,z){let D=[];if(Z.includes("evolution"))D.push("Try the AI Evolution mode to get automated improvement suggestions");if(Z.includes("markdown"))D.push("Use Markdown preview to see documentation for your specs");if(Z.includes("builder"))D.push("Explore the Visual Builder to design your UI components");if(!q.includes("spec_validate")&&q.includes("specs"))D.push("Don't forget to validate your specs before saving");if(q.includes("evolution")&&!q.includes("ai_suggestions"))D.push("Generate AI suggestions to get actionable improvement recommendations");if(z>50)D.push("Great engagement! Consider saving your work regularly");if(X.length===1)D.push("Try different modes to explore all sandbox capabilities");return D}function C(q){let Z=q?.id??"unknown";if(!q)return e(Z);switch(Z){case"crm-pipeline":return Kq(q.schema.contracts);case"saas-boilerplate":return Yq(q.schema.contracts);case"agent-console":return Fq(q.schema.contracts);case"todos-app":return Lq(q.schema.contracts);case"messaging-app":return Nq(q.schema.contracts);case"recipe-app-i18n":return Bq(q.schema.contracts);default:return e(Z)}}function Kq(q){return`// CRM Pipeline Specs
|
|
2
|
+
// Contracts: ${q.join(", ")}
|
|
458
3
|
|
|
459
4
|
contractSpec("crm.deal.updateStage.v1", {
|
|
460
5
|
goal: "Move a deal to a different pipeline stage",
|
|
@@ -523,11 +68,8 @@ contractSpec("crm.contact.list.v1", {
|
|
|
523
68
|
hasMore: "boolean"
|
|
524
69
|
}
|
|
525
70
|
}
|
|
526
|
-
})
|
|
527
|
-
}
|
|
528
|
-
function generateSaasBoilerplateSpec(contracts) {
|
|
529
|
-
return `// SaaS Boilerplate Specs
|
|
530
|
-
// Contracts: ${contracts.join(", ")}
|
|
71
|
+
});`}function Yq(q){return`// SaaS Boilerplate Specs
|
|
72
|
+
// Contracts: ${q.join(", ")}
|
|
531
73
|
|
|
532
74
|
contractSpec("saas.project.create.v1", {
|
|
533
75
|
goal: "Create a new project in an organization",
|
|
@@ -590,11 +132,8 @@ contractSpec("saas.settings.update.v1", {
|
|
|
590
132
|
}
|
|
591
133
|
},
|
|
592
134
|
events: ["settings.updated"]
|
|
593
|
-
})
|
|
594
|
-
}
|
|
595
|
-
function generateAgentConsoleSpec(contracts) {
|
|
596
|
-
return `// Agent Console Specs
|
|
597
|
-
// Contracts: ${contracts.join(", ")}
|
|
135
|
+
});`}function Fq(q){return`// Agent Console Specs
|
|
136
|
+
// Contracts: ${q.join(", ")}
|
|
598
137
|
|
|
599
138
|
contractSpec("agent.run.execute.v1", {
|
|
600
139
|
goal: "Execute an agent run with specified tools",
|
|
@@ -660,11 +199,8 @@ contractSpec("agent.agent.create.v1", {
|
|
|
660
199
|
}
|
|
661
200
|
},
|
|
662
201
|
events: ["agent.created"]
|
|
663
|
-
})
|
|
664
|
-
}
|
|
665
|
-
function generateTodosSpec(contracts) {
|
|
666
|
-
return `// To-dos App Specs
|
|
667
|
-
// Contracts: ${contracts.join(", ")}
|
|
202
|
+
});`}function Lq(q){return`// To-dos App Specs
|
|
203
|
+
// Contracts: ${q.join(", ")}
|
|
668
204
|
|
|
669
205
|
contractSpec("tasks.board.v1", {
|
|
670
206
|
goal: "Assign and approve craft work",
|
|
@@ -723,11 +259,8 @@ contractSpec("tasks.complete.v1", {
|
|
|
723
259
|
}
|
|
724
260
|
},
|
|
725
261
|
events: ["task.completed"]
|
|
726
|
-
})
|
|
727
|
-
}
|
|
728
|
-
function generateMessagingSpec(contracts) {
|
|
729
|
-
return `// Messaging App Specs
|
|
730
|
-
// Contracts: ${contracts.join(", ")}
|
|
262
|
+
});`}function Nq(q){return`// Messaging App Specs
|
|
263
|
+
// Contracts: ${q.join(", ")}
|
|
731
264
|
|
|
732
265
|
contractSpec("messaging.send.v1", {
|
|
733
266
|
goal: "Deliver intent-rich updates",
|
|
@@ -780,11 +313,8 @@ contractSpec("messaging.read.v1", {
|
|
|
780
313
|
}
|
|
781
314
|
},
|
|
782
315
|
events: ["message.read"]
|
|
783
|
-
})
|
|
784
|
-
}
|
|
785
|
-
function generateRecipeSpec(contracts) {
|
|
786
|
-
return `// Recipe App (i18n) Specs
|
|
787
|
-
// Contracts: ${contracts.join(", ")}
|
|
316
|
+
});`}function Bq(q){return`// Recipe App (i18n) Specs
|
|
317
|
+
// Contracts: ${q.join(", ")}
|
|
788
318
|
|
|
789
319
|
contractSpec("recipes.lookup.v1", {
|
|
790
320
|
goal: "Serve bilingual rituals",
|
|
@@ -831,13 +361,10 @@ contractSpec("recipes.favorite.toggle.v1", {
|
|
|
831
361
|
}
|
|
832
362
|
},
|
|
833
363
|
events: ["recipe.favorited", "recipe.unfavorited"]
|
|
834
|
-
})
|
|
835
|
-
}
|
|
836
|
-
function generateDefaultSpec(templateId) {
|
|
837
|
-
return `// ${templateId} Specs
|
|
364
|
+
});`}function e(q){return`// ${q} Specs
|
|
838
365
|
|
|
839
|
-
contractSpec("${
|
|
840
|
-
goal: "Main operation for ${
|
|
366
|
+
contractSpec("${q}.main.v1", {
|
|
367
|
+
goal: "Main operation for ${q}",
|
|
841
368
|
transport: { gql: { query: "main" } },
|
|
842
369
|
io: {
|
|
843
370
|
input: {
|
|
@@ -847,676 +374,38 @@ contractSpec("${templateId}.main.v1", {
|
|
|
847
374
|
result: "unknown"
|
|
848
375
|
}
|
|
849
376
|
}
|
|
850
|
-
})
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
const [loading, setLoading] = useState3(true);
|
|
862
|
-
const [validation, setValidation] = useState3(null);
|
|
863
|
-
const [lastSaved, setLastSaved] = useState3(null);
|
|
864
|
-
useEffect3(() => {
|
|
865
|
-
setLoading(true);
|
|
866
|
-
try {
|
|
867
|
-
const stored = localStorage.getItem(`${SPEC_STORAGE_KEY}-${templateId}`);
|
|
868
|
-
if (stored) {
|
|
869
|
-
const parsed = JSON.parse(stored);
|
|
870
|
-
if (parsed.content) {
|
|
871
|
-
setContentState(parsed.content);
|
|
872
|
-
setSavedContent(parsed.content);
|
|
873
|
-
setLastSaved(parsed.savedAt);
|
|
874
|
-
} else {
|
|
875
|
-
const generated = generateSpecFromTemplate(template);
|
|
876
|
-
setContentState(generated);
|
|
877
|
-
setSavedContent(generated);
|
|
878
|
-
}
|
|
879
|
-
} else {
|
|
880
|
-
const generated = generateSpecFromTemplate(template);
|
|
881
|
-
setContentState(generated);
|
|
882
|
-
setSavedContent(generated);
|
|
883
|
-
}
|
|
884
|
-
} catch {
|
|
885
|
-
const generated = generateSpecFromTemplate(template);
|
|
886
|
-
setContentState(generated);
|
|
887
|
-
setSavedContent(generated);
|
|
888
|
-
}
|
|
889
|
-
setLoading(false);
|
|
890
|
-
}, [templateId]);
|
|
891
|
-
const setContent = useCallback3((newContent) => {
|
|
892
|
-
setContentState(newContent);
|
|
893
|
-
setValidation(null);
|
|
894
|
-
}, []);
|
|
895
|
-
const save = useCallback3(() => {
|
|
896
|
-
try {
|
|
897
|
-
const savedAt = new Date().toISOString();
|
|
898
|
-
localStorage.setItem(`${SPEC_STORAGE_KEY}-${templateId}`, JSON.stringify({
|
|
899
|
-
content,
|
|
900
|
-
savedAt
|
|
901
|
-
}));
|
|
902
|
-
setSavedContent(content);
|
|
903
|
-
setLastSaved(savedAt);
|
|
904
|
-
} catch {}
|
|
905
|
-
}, [content, templateId]);
|
|
906
|
-
const validate = useCallback3(() => {
|
|
907
|
-
const errors = [];
|
|
908
|
-
const lines = content.split(`
|
|
909
|
-
`);
|
|
910
|
-
if (!content.includes("contractSpec(")) {
|
|
911
|
-
errors.push({
|
|
912
|
-
line: 1,
|
|
913
|
-
message: "Spec must contain a contractSpec() definition",
|
|
914
|
-
severity: "error"
|
|
915
|
-
});
|
|
916
|
-
}
|
|
917
|
-
if (!content.includes("goal:")) {
|
|
918
|
-
errors.push({
|
|
919
|
-
line: 1,
|
|
920
|
-
message: "Spec should have a goal field",
|
|
921
|
-
severity: "warning"
|
|
922
|
-
});
|
|
923
|
-
}
|
|
924
|
-
if (!content.includes("io:")) {
|
|
925
|
-
errors.push({
|
|
926
|
-
line: 1,
|
|
927
|
-
message: "Spec should define io (input/output)",
|
|
928
|
-
severity: "warning"
|
|
929
|
-
});
|
|
930
|
-
}
|
|
931
|
-
const openBraces = (content.match(/{/g) ?? []).length;
|
|
932
|
-
const closeBraces = (content.match(/}/g) ?? []).length;
|
|
933
|
-
if (openBraces !== closeBraces) {
|
|
934
|
-
errors.push({
|
|
935
|
-
line: lines.length,
|
|
936
|
-
message: `Unbalanced braces: ${openBraces} opening, ${closeBraces} closing`,
|
|
937
|
-
severity: "error"
|
|
938
|
-
});
|
|
939
|
-
}
|
|
940
|
-
const openParens = (content.match(/\(/g) ?? []).length;
|
|
941
|
-
const closeParens = (content.match(/\)/g) ?? []).length;
|
|
942
|
-
if (openParens !== closeParens) {
|
|
943
|
-
errors.push({
|
|
944
|
-
line: lines.length,
|
|
945
|
-
message: `Unbalanced parentheses: ${openParens} opening, ${closeParens} closing`,
|
|
946
|
-
severity: "error"
|
|
947
|
-
});
|
|
948
|
-
}
|
|
949
|
-
lines.forEach((line, index) => {
|
|
950
|
-
const singleQuotes = (line.match(/'/g) ?? []).length;
|
|
951
|
-
const doubleQuotes = (line.match(/"/g) ?? []).length;
|
|
952
|
-
if (singleQuotes % 2 !== 0) {
|
|
953
|
-
errors.push({
|
|
954
|
-
line: index + 1,
|
|
955
|
-
message: "Unclosed single quote",
|
|
956
|
-
severity: "error"
|
|
957
|
-
});
|
|
958
|
-
}
|
|
959
|
-
if (doubleQuotes % 2 !== 0) {
|
|
960
|
-
errors.push({
|
|
961
|
-
line: index + 1,
|
|
962
|
-
message: "Unclosed double quote",
|
|
963
|
-
severity: "error"
|
|
964
|
-
});
|
|
965
|
-
}
|
|
966
|
-
});
|
|
967
|
-
const result = {
|
|
968
|
-
valid: errors.filter((e) => e.severity === "error").length === 0,
|
|
969
|
-
errors
|
|
970
|
-
};
|
|
971
|
-
setValidation(result);
|
|
972
|
-
return result;
|
|
973
|
-
}, [content]);
|
|
974
|
-
const reset = useCallback3(() => {
|
|
975
|
-
const generated = generateSpecFromTemplate(template);
|
|
976
|
-
setContentState(generated);
|
|
977
|
-
setSavedContent(generated);
|
|
978
|
-
setValidation(null);
|
|
979
|
-
setLastSaved(null);
|
|
980
|
-
try {
|
|
981
|
-
localStorage.removeItem(`${SPEC_STORAGE_KEY}-${templateId}`);
|
|
982
|
-
} catch {}
|
|
983
|
-
}, [templateId]);
|
|
984
|
-
return {
|
|
985
|
-
content,
|
|
986
|
-
loading,
|
|
987
|
-
isDirty: content !== savedContent,
|
|
988
|
-
validation,
|
|
989
|
-
setContent,
|
|
990
|
-
save,
|
|
991
|
-
validate,
|
|
992
|
-
reset,
|
|
993
|
-
lastSaved
|
|
994
|
-
};
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
// src/hooks/useRegistryTemplates.ts
|
|
998
|
-
import { useQuery } from "@tanstack/react-query";
|
|
999
|
-
function useRegistryTemplates() {
|
|
1000
|
-
return useQuery({
|
|
1001
|
-
queryKey: ["registryTemplates"],
|
|
1002
|
-
queryFn: async () => {
|
|
1003
|
-
const registryUrl = process.env.NEXT_PUBLIC_CONTRACTSPEC_REGISTRY_URL ?? "";
|
|
1004
|
-
if (!registryUrl)
|
|
1005
|
-
return [];
|
|
1006
|
-
const res = await fetch(`${registryUrl.replace(/\/$/, "")}/r/contractspec.json`, {
|
|
1007
|
-
method: "GET",
|
|
1008
|
-
headers: { Accept: "application/json" }
|
|
1009
|
-
});
|
|
1010
|
-
if (!res.ok)
|
|
1011
|
-
return [];
|
|
1012
|
-
const json = await res.json();
|
|
1013
|
-
const items = json.items ?? [];
|
|
1014
|
-
return items.filter((i) => i.type === "contractspec:template").map((i) => ({
|
|
1015
|
-
id: i.name,
|
|
1016
|
-
name: i.title ?? i.name,
|
|
1017
|
-
description: i.description,
|
|
1018
|
-
tags: i.meta?.tags ?? [],
|
|
1019
|
-
source: "registry",
|
|
1020
|
-
registryUrl
|
|
1021
|
-
}));
|
|
1022
|
-
}
|
|
1023
|
-
});
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
// src/hooks/useWorkflowComposer.ts
|
|
1027
|
-
import { useCallback as useCallback4, useEffect as useEffect4, useMemo as useMemo3, useState as useState4 } from "react";
|
|
1028
|
-
"use client";
|
|
1029
|
-
function useWorkflowComposer(templateId) {
|
|
1030
|
-
const [selectedWorkflow, setSelectedWorkflow] = useState4(null);
|
|
1031
|
-
const [extensions, setExtensions] = useState4([]);
|
|
1032
|
-
const [loading, _setLoading] = useState4(false);
|
|
1033
|
-
const [error, _setError] = useState4(null);
|
|
1034
|
-
const baseWorkflows = useMemo3(() => getTemplateWorkflows(templateId), [templateId]);
|
|
1035
|
-
useEffect4(() => {
|
|
1036
|
-
const firstWorkflow = baseWorkflows[0];
|
|
1037
|
-
if (baseWorkflows.length > 0 && !selectedWorkflow && firstWorkflow) {
|
|
1038
|
-
setSelectedWorkflow(firstWorkflow.meta.key);
|
|
1039
|
-
}
|
|
1040
|
-
}, [baseWorkflows, selectedWorkflow]);
|
|
1041
|
-
const currentBase = useMemo3(() => {
|
|
1042
|
-
return baseWorkflows.find((w) => w.meta.key === selectedWorkflow) ?? null;
|
|
1043
|
-
}, [baseWorkflows, selectedWorkflow]);
|
|
1044
|
-
const compose = useCallback4((scope) => {
|
|
1045
|
-
if (!currentBase)
|
|
1046
|
-
return null;
|
|
1047
|
-
const applicableExtensions = extensions.filter((ext) => ext.workflow === currentBase.meta.key).filter((ext) => matchesScope(ext, scope)).sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
|
|
1048
|
-
if (applicableExtensions.length === 0) {
|
|
1049
|
-
return currentBase;
|
|
1050
|
-
}
|
|
1051
|
-
let composedWorkflow = { ...currentBase, steps: [...currentBase.steps] };
|
|
1052
|
-
for (const extension of applicableExtensions) {
|
|
1053
|
-
composedWorkflow = applyExtension(composedWorkflow, extension);
|
|
1054
|
-
}
|
|
1055
|
-
return composedWorkflow;
|
|
1056
|
-
}, [currentBase, extensions]);
|
|
1057
|
-
const workflow = useMemo3(() => compose(), [compose]);
|
|
1058
|
-
const selectWorkflow = useCallback4((workflowName) => {
|
|
1059
|
-
setSelectedWorkflow(workflowName);
|
|
1060
|
-
}, []);
|
|
1061
|
-
const addExtension = useCallback4((extension) => {
|
|
1062
|
-
setExtensions((prev) => [...prev, extension]);
|
|
1063
|
-
}, []);
|
|
1064
|
-
const removeExtension = useCallback4((workflowName, index) => {
|
|
1065
|
-
setExtensions((prev) => {
|
|
1066
|
-
const forWorkflow = prev.filter((e) => e.workflow === workflowName);
|
|
1067
|
-
const others = prev.filter((e) => e.workflow !== workflowName);
|
|
1068
|
-
forWorkflow.splice(index, 1);
|
|
1069
|
-
return [...others, ...forWorkflow];
|
|
1070
|
-
});
|
|
1071
|
-
}, []);
|
|
1072
|
-
const generateSpecCode = useCallback4(() => {
|
|
1073
|
-
const composed = workflow;
|
|
1074
|
-
if (!composed) {
|
|
1075
|
-
return "// No workflow selected";
|
|
1076
|
-
}
|
|
1077
|
-
const stepsCode = composed.steps.map((step) => ` {
|
|
1078
|
-
id: '${step.id}',
|
|
1079
|
-
name: '${step.name}',
|
|
1080
|
-
type: '${step.type}',${step.description ? `
|
|
1081
|
-
description: '${step.description}',` : ""}${step.next ? `
|
|
1082
|
-
next: ${JSON.stringify(step.next)},` : ""}${step.condition ? `
|
|
1083
|
-
condition: '${step.condition}',` : ""}${step.timeout ? `
|
|
1084
|
-
timeout: ${step.timeout},` : ""}${step.retries ? `
|
|
1085
|
-
retries: ${step.retries},` : ""}${step.onError ? `
|
|
1086
|
-
onError: '${step.onError}',` : ""}
|
|
377
|
+
});`}import{useCallback as v,useEffect as Mq,useState as g}from"react";var p="contractspec-spec-content";function Sq(q){let{template:Z}=s(),[X,z]=g(""),[D,H]=g(""),[B,_]=g(!0),[T,L]=g(null),[j,G]=g(null);Mq(()=>{_(!0);try{let h=localStorage.getItem(`${p}-${q}`);if(h){let Q=JSON.parse(h);if(Q.content)z(Q.content),H(Q.content),G(Q.savedAt);else{let K=C(Z);z(K),H(K)}}else{let Q=C(Z);z(Q),H(Q)}}catch{let h=C(Z);z(h),H(h)}_(!1)},[q]);let F=v((h)=>{z(h),L(null)},[]),W=v(()=>{try{let h=new Date().toISOString();localStorage.setItem(`${p}-${q}`,JSON.stringify({content:X,savedAt:h})),H(X),G(h)}catch{}},[X,q]),O=v(()=>{let h=[],Q=X.split(`
|
|
378
|
+
`);if(!X.includes("contractSpec("))h.push({line:1,message:"Spec must contain a contractSpec() definition",severity:"error"});if(!X.includes("goal:"))h.push({line:1,message:"Spec should have a goal field",severity:"warning"});if(!X.includes("io:"))h.push({line:1,message:"Spec should define io (input/output)",severity:"warning"});let K=(X.match(/{/g)??[]).length,N=(X.match(/}/g)??[]).length;if(K!==N)h.push({line:Q.length,message:`Unbalanced braces: ${K} opening, ${N} closing`,severity:"error"});let $=(X.match(/\(/g)??[]).length,J=(X.match(/\)/g)??[]).length;if($!==J)h.push({line:Q.length,message:`Unbalanced parentheses: ${$} opening, ${J} closing`,severity:"error"});Q.forEach((Y,E)=>{let V=(Y.match(/'/g)??[]).length,U=(Y.match(/"/g)??[]).length;if(V%2!==0)h.push({line:E+1,message:"Unclosed single quote",severity:"error"});if(U%2!==0)h.push({line:E+1,message:"Unclosed double quote",severity:"error"})});let P={valid:h.filter((Y)=>Y.severity==="error").length===0,errors:h};return L(P),P},[X]),A=v(()=>{let h=C(Z);z(h),H(h),L(null),G(null);try{localStorage.removeItem(`${p}-${q}`)}catch{}},[q]);return{content:X,loading:B,isDirty:X!==D,validation:T,setContent:F,save:W,validate:O,reset:A,lastSaved:j}}import{useQuery as Wq}from"@tanstack/react-query";function cq(){return Wq({queryKey:["registryTemplates"],queryFn:async()=>{let q=process.env.NEXT_PUBLIC_CONTRACTSPEC_REGISTRY_URL??"";if(!q)return[];let Z=await fetch(`${q.replace(/\/$/,"")}/r/contractspec.json`,{method:"GET",headers:{Accept:"application/json"}});if(!Z.ok)return[];return((await Z.json()).items??[]).filter((D)=>D.type==="contractspec:template").map((D)=>({id:D.name,name:D.title??D.name,description:D.description,tags:D.meta?.tags??[],source:"registry",registryUrl:q}))}})}import{useCallback as y,useEffect as _q,useMemo as r,useState as S}from"react";function rq(q){let[Z,X]=S(null),[z,D]=S([]),[H,B]=S(!1),[_,T]=S(null),L=r(()=>Tq(q),[q]);_q(()=>{let Q=L[0];if(L.length>0&&!Z&&Q)X(Q.meta.key)},[L,Z]);let j=r(()=>{return L.find((Q)=>Q.meta.key===Z)??null},[L,Z]),G=y((Q)=>{if(!j)return null;let K=z.filter(($)=>$.workflow===j.meta.key).filter(($)=>Oq($,Q)).sort(($,J)=>($.priority??0)-(J.priority??0));if(K.length===0)return j;let N={...j,steps:[...j.steps]};for(let $ of K)N=Aq(N,$);return N},[j,z]),F=r(()=>G(),[G]),W=y((Q)=>{X(Q)},[]),O=y((Q)=>{D((K)=>[...K,Q])},[]),A=y((Q,K)=>{D((N)=>{let $=N.filter((P)=>P.workflow===Q),J=N.filter((P)=>P.workflow!==Q);return $.splice(K,1),[...J,...$]})},[]),h=y(()=>{let Q=F;if(!Q)return"// No workflow selected";let K=Q.steps.map(($)=>` {
|
|
379
|
+
id: '${$.id}',
|
|
380
|
+
name: '${$.name}',
|
|
381
|
+
type: '${$.type}',${$.description?`
|
|
382
|
+
description: '${$.description}',`:""}${$.next?`
|
|
383
|
+
next: ${JSON.stringify($.next)},`:""}${$.condition?`
|
|
384
|
+
condition: '${$.condition}',`:""}${$.timeout?`
|
|
385
|
+
timeout: ${$.timeout},`:""}${$.retries?`
|
|
386
|
+
retries: ${$.retries},`:""}${$.onError?`
|
|
387
|
+
onError: '${$.onError}',`:""}
|
|
1087
388
|
}`).join(`,
|
|
1088
|
-
`)
|
|
1089
|
-
const extensionsCode = extensions.length > 0 ? `
|
|
389
|
+
`),N=z.length>0?`
|
|
1090
390
|
|
|
1091
391
|
// Extensions applied:
|
|
1092
|
-
${
|
|
1093
|
-
`)}
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
${extensionsCode}
|
|
392
|
+
${z.map(($)=>`// - ${$.workflow} (priority: ${$.priority??0})${$.customSteps?.length?` +${$.customSteps.length} steps`:""}${$.hiddenSteps?.length?` -${$.hiddenSteps.length} hidden`:""}`).join(`
|
|
393
|
+
`)}`:"";return`// Workflow Spec: ${Q.meta.key} v${Q.meta.version}
|
|
394
|
+
// Generated from ${q} template
|
|
395
|
+
${N}
|
|
1097
396
|
|
|
1098
397
|
import { workflowSpec } from '@contractspec/lib.contracts-spec/workflow';
|
|
1099
398
|
|
|
1100
|
-
export const ${
|
|
399
|
+
export const ${Uq(Q.meta.key)}Workflow = workflowSpec({
|
|
1101
400
|
meta: {
|
|
1102
|
-
key: '${
|
|
1103
|
-
version: ${
|
|
1104
|
-
description: '${
|
|
401
|
+
key: '${Q.meta.key}',
|
|
402
|
+
version: ${Q.meta.version},${Q.meta.description?`
|
|
403
|
+
description: '${Q.meta.description}',`:""}
|
|
1105
404
|
},
|
|
1106
|
-
start: '${
|
|
405
|
+
start: '${Q.start}',
|
|
1107
406
|
steps: [
|
|
1108
|
-
${
|
|
1109
|
-
],${
|
|
1110
|
-
context: ${JSON.stringify(
|
|
407
|
+
${K}
|
|
408
|
+
],${Q.context?`
|
|
409
|
+
context: ${JSON.stringify(Q.context,null,2)},`:""}
|
|
1111
410
|
});
|
|
1112
|
-
|
|
1113
|
-
}, [workflow, extensions, templateId]);
|
|
1114
|
-
return {
|
|
1115
|
-
workflow,
|
|
1116
|
-
baseWorkflows,
|
|
1117
|
-
extensions,
|
|
1118
|
-
selectWorkflow,
|
|
1119
|
-
addExtension,
|
|
1120
|
-
removeExtension,
|
|
1121
|
-
compose,
|
|
1122
|
-
generateSpecCode,
|
|
1123
|
-
loading,
|
|
1124
|
-
error
|
|
1125
|
-
};
|
|
1126
|
-
}
|
|
1127
|
-
function matchesScope(extension, scope) {
|
|
1128
|
-
if (!scope)
|
|
1129
|
-
return true;
|
|
1130
|
-
if (extension.tenantId && extension.tenantId !== scope.tenantId) {
|
|
1131
|
-
return false;
|
|
1132
|
-
}
|
|
1133
|
-
if (extension.role && extension.role !== scope.role) {
|
|
1134
|
-
return false;
|
|
1135
|
-
}
|
|
1136
|
-
if (extension.device && extension.device !== scope.device) {
|
|
1137
|
-
return false;
|
|
1138
|
-
}
|
|
1139
|
-
return true;
|
|
1140
|
-
}
|
|
1141
|
-
function applyExtension(workflow, extension) {
|
|
1142
|
-
let steps = [...workflow.steps];
|
|
1143
|
-
if (extension.hiddenSteps) {
|
|
1144
|
-
steps = steps.filter((s) => !extension.hiddenSteps?.includes(s.id));
|
|
1145
|
-
}
|
|
1146
|
-
if (extension.customSteps) {
|
|
1147
|
-
for (const injection of extension.customSteps) {
|
|
1148
|
-
const stepToInject = {
|
|
1149
|
-
...injection.inject,
|
|
1150
|
-
id: injection.id ?? injection.inject.id
|
|
1151
|
-
};
|
|
1152
|
-
if (injection.after) {
|
|
1153
|
-
const afterIndex = steps.findIndex((s) => s.id === injection.after);
|
|
1154
|
-
if (afterIndex !== -1) {
|
|
1155
|
-
steps.splice(afterIndex + 1, 0, stepToInject);
|
|
1156
|
-
}
|
|
1157
|
-
} else if (injection.before) {
|
|
1158
|
-
const beforeIndex = steps.findIndex((s) => s.id === injection.before);
|
|
1159
|
-
if (beforeIndex !== -1) {
|
|
1160
|
-
steps.splice(beforeIndex, 0, stepToInject);
|
|
1161
|
-
}
|
|
1162
|
-
} else {
|
|
1163
|
-
steps.push(stepToInject);
|
|
1164
|
-
}
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
|
-
return { ...workflow, steps };
|
|
1168
|
-
}
|
|
1169
|
-
function toCamelCase(str) {
|
|
1170
|
-
return str.replace(/[^a-zA-Z0-9]+(.)/g, (_, c) => c.toUpperCase()).replace(/^./, (c) => c.toLowerCase());
|
|
1171
|
-
}
|
|
1172
|
-
function getTemplateWorkflows(templateId) {
|
|
1173
|
-
const templateWorkflows = {
|
|
1174
|
-
"crm-pipeline": [
|
|
1175
|
-
{
|
|
1176
|
-
meta: {
|
|
1177
|
-
key: "deal.qualification",
|
|
1178
|
-
version: "1.0.0",
|
|
1179
|
-
description: "Deal qualification workflow"
|
|
1180
|
-
},
|
|
1181
|
-
start: "lead-received",
|
|
1182
|
-
steps: [
|
|
1183
|
-
{
|
|
1184
|
-
id: "lead-received",
|
|
1185
|
-
name: "Lead Received",
|
|
1186
|
-
type: "action",
|
|
1187
|
-
description: "New lead enters the pipeline",
|
|
1188
|
-
next: "qualify-lead"
|
|
1189
|
-
},
|
|
1190
|
-
{
|
|
1191
|
-
id: "qualify-lead",
|
|
1192
|
-
name: "Qualify Lead",
|
|
1193
|
-
type: "decision",
|
|
1194
|
-
description: "Determine if lead meets qualification criteria",
|
|
1195
|
-
next: ["qualified", "disqualified"],
|
|
1196
|
-
condition: "lead.score >= threshold"
|
|
1197
|
-
},
|
|
1198
|
-
{
|
|
1199
|
-
id: "qualified",
|
|
1200
|
-
name: "Lead Qualified",
|
|
1201
|
-
type: "action",
|
|
1202
|
-
next: "assign-rep"
|
|
1203
|
-
},
|
|
1204
|
-
{
|
|
1205
|
-
id: "disqualified",
|
|
1206
|
-
name: "Lead Disqualified",
|
|
1207
|
-
type: "end"
|
|
1208
|
-
},
|
|
1209
|
-
{
|
|
1210
|
-
id: "assign-rep",
|
|
1211
|
-
name: "Assign Sales Rep",
|
|
1212
|
-
type: "action",
|
|
1213
|
-
next: "complete"
|
|
1214
|
-
},
|
|
1215
|
-
{
|
|
1216
|
-
id: "complete",
|
|
1217
|
-
name: "Workflow Complete",
|
|
1218
|
-
type: "end"
|
|
1219
|
-
}
|
|
1220
|
-
]
|
|
1221
|
-
},
|
|
1222
|
-
{
|
|
1223
|
-
meta: {
|
|
1224
|
-
key: "deal.closing",
|
|
1225
|
-
version: "1.0.0",
|
|
1226
|
-
description: "Deal closing workflow"
|
|
1227
|
-
},
|
|
1228
|
-
start: "proposal-sent",
|
|
1229
|
-
steps: [
|
|
1230
|
-
{
|
|
1231
|
-
id: "proposal-sent",
|
|
1232
|
-
name: "Proposal Sent",
|
|
1233
|
-
type: "action",
|
|
1234
|
-
next: "wait-response"
|
|
1235
|
-
},
|
|
1236
|
-
{
|
|
1237
|
-
id: "wait-response",
|
|
1238
|
-
name: "Wait for Response",
|
|
1239
|
-
type: "wait",
|
|
1240
|
-
timeout: 604800000,
|
|
1241
|
-
next: "negotiate",
|
|
1242
|
-
onError: "follow-up"
|
|
1243
|
-
},
|
|
1244
|
-
{
|
|
1245
|
-
id: "follow-up",
|
|
1246
|
-
name: "Follow Up",
|
|
1247
|
-
type: "action",
|
|
1248
|
-
next: "wait-response",
|
|
1249
|
-
retries: 3
|
|
1250
|
-
},
|
|
1251
|
-
{
|
|
1252
|
-
id: "negotiate",
|
|
1253
|
-
name: "Negotiation",
|
|
1254
|
-
type: "action",
|
|
1255
|
-
next: "finalize"
|
|
1256
|
-
},
|
|
1257
|
-
{
|
|
1258
|
-
id: "finalize",
|
|
1259
|
-
name: "Finalize Deal",
|
|
1260
|
-
type: "decision",
|
|
1261
|
-
next: ["won", "lost"],
|
|
1262
|
-
condition: "deal.accepted"
|
|
1263
|
-
},
|
|
1264
|
-
{
|
|
1265
|
-
id: "won",
|
|
1266
|
-
name: "Deal Won",
|
|
1267
|
-
type: "end"
|
|
1268
|
-
},
|
|
1269
|
-
{
|
|
1270
|
-
id: "lost",
|
|
1271
|
-
name: "Deal Lost",
|
|
1272
|
-
type: "end"
|
|
1273
|
-
}
|
|
1274
|
-
]
|
|
1275
|
-
}
|
|
1276
|
-
],
|
|
1277
|
-
"saas-boilerplate": [
|
|
1278
|
-
{
|
|
1279
|
-
meta: {
|
|
1280
|
-
key: "user.onboarding",
|
|
1281
|
-
version: "1.0.0",
|
|
1282
|
-
description: "User onboarding workflow"
|
|
1283
|
-
},
|
|
1284
|
-
start: "signup",
|
|
1285
|
-
steps: [
|
|
1286
|
-
{
|
|
1287
|
-
id: "signup",
|
|
1288
|
-
name: "User Signup",
|
|
1289
|
-
type: "action",
|
|
1290
|
-
next: "verify-email"
|
|
1291
|
-
},
|
|
1292
|
-
{
|
|
1293
|
-
id: "verify-email",
|
|
1294
|
-
name: "Verify Email",
|
|
1295
|
-
type: "wait",
|
|
1296
|
-
timeout: 86400000,
|
|
1297
|
-
next: "profile-setup",
|
|
1298
|
-
onError: "resend-verification"
|
|
1299
|
-
},
|
|
1300
|
-
{
|
|
1301
|
-
id: "resend-verification",
|
|
1302
|
-
name: "Resend Verification",
|
|
1303
|
-
type: "action",
|
|
1304
|
-
next: "verify-email",
|
|
1305
|
-
retries: 2
|
|
1306
|
-
},
|
|
1307
|
-
{
|
|
1308
|
-
id: "profile-setup",
|
|
1309
|
-
name: "Setup Profile",
|
|
1310
|
-
type: "action",
|
|
1311
|
-
next: "onboarding-tour"
|
|
1312
|
-
},
|
|
1313
|
-
{
|
|
1314
|
-
id: "onboarding-tour",
|
|
1315
|
-
name: "Onboarding Tour",
|
|
1316
|
-
type: "action",
|
|
1317
|
-
next: "complete"
|
|
1318
|
-
},
|
|
1319
|
-
{
|
|
1320
|
-
id: "complete",
|
|
1321
|
-
name: "Onboarding Complete",
|
|
1322
|
-
type: "end"
|
|
1323
|
-
}
|
|
1324
|
-
]
|
|
1325
|
-
}
|
|
1326
|
-
],
|
|
1327
|
-
"agent-console": [
|
|
1328
|
-
{
|
|
1329
|
-
meta: {
|
|
1330
|
-
key: "agent.execution",
|
|
1331
|
-
version: "1.0.0",
|
|
1332
|
-
description: "Agent execution workflow"
|
|
1333
|
-
},
|
|
1334
|
-
start: "receive-task",
|
|
1335
|
-
steps: [
|
|
1336
|
-
{
|
|
1337
|
-
id: "receive-task",
|
|
1338
|
-
name: "Receive Task",
|
|
1339
|
-
type: "action",
|
|
1340
|
-
next: "plan-execution"
|
|
1341
|
-
},
|
|
1342
|
-
{
|
|
1343
|
-
id: "plan-execution",
|
|
1344
|
-
name: "Plan Execution",
|
|
1345
|
-
type: "action",
|
|
1346
|
-
next: "execute-steps"
|
|
1347
|
-
},
|
|
1348
|
-
{
|
|
1349
|
-
id: "execute-steps",
|
|
1350
|
-
name: "Execute Steps",
|
|
1351
|
-
type: "parallel",
|
|
1352
|
-
next: ["tool-call", "observe", "reason"]
|
|
1353
|
-
},
|
|
1354
|
-
{
|
|
1355
|
-
id: "tool-call",
|
|
1356
|
-
name: "Tool Call",
|
|
1357
|
-
type: "action",
|
|
1358
|
-
next: "evaluate"
|
|
1359
|
-
},
|
|
1360
|
-
{
|
|
1361
|
-
id: "observe",
|
|
1362
|
-
name: "Observe",
|
|
1363
|
-
type: "action",
|
|
1364
|
-
next: "evaluate"
|
|
1365
|
-
},
|
|
1366
|
-
{
|
|
1367
|
-
id: "reason",
|
|
1368
|
-
name: "Reason",
|
|
1369
|
-
type: "action",
|
|
1370
|
-
next: "evaluate"
|
|
1371
|
-
},
|
|
1372
|
-
{
|
|
1373
|
-
id: "evaluate",
|
|
1374
|
-
name: "Evaluate Result",
|
|
1375
|
-
type: "decision",
|
|
1376
|
-
condition: "task.isComplete",
|
|
1377
|
-
next: ["complete", "execute-steps"]
|
|
1378
|
-
},
|
|
1379
|
-
{
|
|
1380
|
-
id: "complete",
|
|
1381
|
-
name: "Task Complete",
|
|
1382
|
-
type: "end"
|
|
1383
|
-
}
|
|
1384
|
-
]
|
|
1385
|
-
}
|
|
1386
|
-
],
|
|
1387
|
-
"todos-app": [
|
|
1388
|
-
{
|
|
1389
|
-
meta: {
|
|
1390
|
-
key: "task.lifecycle",
|
|
1391
|
-
version: "1.0.0",
|
|
1392
|
-
description: "Task lifecycle workflow"
|
|
1393
|
-
},
|
|
1394
|
-
start: "created",
|
|
1395
|
-
steps: [
|
|
1396
|
-
{
|
|
1397
|
-
id: "created",
|
|
1398
|
-
name: "Task Created",
|
|
1399
|
-
type: "action",
|
|
1400
|
-
next: "in-progress"
|
|
1401
|
-
},
|
|
1402
|
-
{
|
|
1403
|
-
id: "in-progress",
|
|
1404
|
-
name: "In Progress",
|
|
1405
|
-
type: "action",
|
|
1406
|
-
next: "review"
|
|
1407
|
-
},
|
|
1408
|
-
{
|
|
1409
|
-
id: "review",
|
|
1410
|
-
name: "Review",
|
|
1411
|
-
type: "decision",
|
|
1412
|
-
condition: "task.approved",
|
|
1413
|
-
next: ["done", "in-progress"]
|
|
1414
|
-
},
|
|
1415
|
-
{
|
|
1416
|
-
id: "done",
|
|
1417
|
-
name: "Done",
|
|
1418
|
-
type: "end"
|
|
1419
|
-
}
|
|
1420
|
-
]
|
|
1421
|
-
}
|
|
1422
|
-
],
|
|
1423
|
-
"messaging-app": [
|
|
1424
|
-
{
|
|
1425
|
-
meta: {
|
|
1426
|
-
key: "message.delivery",
|
|
1427
|
-
version: "1.0.0",
|
|
1428
|
-
description: "Message delivery workflow"
|
|
1429
|
-
},
|
|
1430
|
-
start: "compose",
|
|
1431
|
-
steps: [
|
|
1432
|
-
{
|
|
1433
|
-
id: "compose",
|
|
1434
|
-
name: "Compose Message",
|
|
1435
|
-
type: "action",
|
|
1436
|
-
next: "send"
|
|
1437
|
-
},
|
|
1438
|
-
{
|
|
1439
|
-
id: "send",
|
|
1440
|
-
name: "Send Message",
|
|
1441
|
-
type: "action",
|
|
1442
|
-
next: "deliver"
|
|
1443
|
-
},
|
|
1444
|
-
{
|
|
1445
|
-
id: "deliver",
|
|
1446
|
-
name: "Deliver",
|
|
1447
|
-
type: "decision",
|
|
1448
|
-
condition: "recipient.online",
|
|
1449
|
-
next: ["delivered", "queue"]
|
|
1450
|
-
},
|
|
1451
|
-
{
|
|
1452
|
-
id: "queue",
|
|
1453
|
-
name: "Queue for Delivery",
|
|
1454
|
-
type: "wait",
|
|
1455
|
-
next: "deliver"
|
|
1456
|
-
},
|
|
1457
|
-
{
|
|
1458
|
-
id: "delivered",
|
|
1459
|
-
name: "Message Delivered",
|
|
1460
|
-
type: "action",
|
|
1461
|
-
next: "read"
|
|
1462
|
-
},
|
|
1463
|
-
{
|
|
1464
|
-
id: "read",
|
|
1465
|
-
name: "Message Read",
|
|
1466
|
-
type: "end"
|
|
1467
|
-
}
|
|
1468
|
-
]
|
|
1469
|
-
}
|
|
1470
|
-
],
|
|
1471
|
-
"recipe-app-i18n": [
|
|
1472
|
-
{
|
|
1473
|
-
meta: {
|
|
1474
|
-
key: "recipe.creation",
|
|
1475
|
-
version: "1.0.0",
|
|
1476
|
-
description: "Recipe creation workflow"
|
|
1477
|
-
},
|
|
1478
|
-
start: "draft",
|
|
1479
|
-
steps: [
|
|
1480
|
-
{
|
|
1481
|
-
id: "draft",
|
|
1482
|
-
name: "Draft Recipe",
|
|
1483
|
-
type: "action",
|
|
1484
|
-
next: "add-ingredients"
|
|
1485
|
-
},
|
|
1486
|
-
{
|
|
1487
|
-
id: "add-ingredients",
|
|
1488
|
-
name: "Add Ingredients",
|
|
1489
|
-
type: "action",
|
|
1490
|
-
next: "add-steps"
|
|
1491
|
-
},
|
|
1492
|
-
{
|
|
1493
|
-
id: "add-steps",
|
|
1494
|
-
name: "Add Steps",
|
|
1495
|
-
type: "action",
|
|
1496
|
-
next: "review"
|
|
1497
|
-
},
|
|
1498
|
-
{
|
|
1499
|
-
id: "review",
|
|
1500
|
-
name: "Review Recipe",
|
|
1501
|
-
type: "decision",
|
|
1502
|
-
condition: "recipe.isComplete",
|
|
1503
|
-
next: ["publish", "draft"]
|
|
1504
|
-
},
|
|
1505
|
-
{
|
|
1506
|
-
id: "publish",
|
|
1507
|
-
name: "Publish Recipe",
|
|
1508
|
-
type: "end"
|
|
1509
|
-
}
|
|
1510
|
-
]
|
|
1511
|
-
}
|
|
1512
|
-
]
|
|
1513
|
-
};
|
|
1514
|
-
return templateWorkflows[templateId] ?? [];
|
|
1515
|
-
}
|
|
1516
|
-
export {
|
|
1517
|
-
useWorkflowComposer,
|
|
1518
|
-
useSpecContent,
|
|
1519
|
-
useRegistryTemplates,
|
|
1520
|
-
useEvolution,
|
|
1521
|
-
useBehaviorTracking
|
|
1522
|
-
};
|
|
411
|
+
`},[F,z,q]);return{workflow:F,baseWorkflows:L,extensions:z,selectWorkflow:W,addExtension:O,removeExtension:A,compose:G,generateSpecCode:h,loading:H,error:_}}function Oq(q,Z){if(!Z)return!0;if(q.tenantId&&q.tenantId!==Z.tenantId)return!1;if(q.role&&q.role!==Z.role)return!1;if(q.device&&q.device!==Z.device)return!1;return!0}function Aq(q,Z){let X=[...q.steps];if(Z.hiddenSteps)X=X.filter((z)=>!Z.hiddenSteps?.includes(z.id));if(Z.customSteps)for(let z of Z.customSteps){let D={...z.inject,id:z.id??z.inject.id};if(z.after){let H=X.findIndex((B)=>B.id===z.after);if(H!==-1)X.splice(H+1,0,D)}else if(z.before){let H=X.findIndex((B)=>B.id===z.before);if(H!==-1)X.splice(H,0,D)}else X.push(D)}return{...q,steps:X}}function Uq(q){return q.replace(/[^a-zA-Z0-9]+(.)/g,(Z,X)=>X.toUpperCase()).replace(/^./,(Z)=>Z.toLowerCase())}function Tq(q){return{"crm-pipeline":[{meta:{key:"deal.qualification",version:"1.0.0",description:"Deal qualification workflow"},start:"lead-received",steps:[{id:"lead-received",name:"Lead Received",type:"action",description:"New lead enters the pipeline",next:"qualify-lead"},{id:"qualify-lead",name:"Qualify Lead",type:"decision",description:"Determine if lead meets qualification criteria",next:["qualified","disqualified"],condition:"lead.score >= threshold"},{id:"qualified",name:"Lead Qualified",type:"action",next:"assign-rep"},{id:"disqualified",name:"Lead Disqualified",type:"end"},{id:"assign-rep",name:"Assign Sales Rep",type:"action",next:"complete"},{id:"complete",name:"Workflow Complete",type:"end"}]},{meta:{key:"deal.closing",version:"1.0.0",description:"Deal closing workflow"},start:"proposal-sent",steps:[{id:"proposal-sent",name:"Proposal Sent",type:"action",next:"wait-response"},{id:"wait-response",name:"Wait for Response",type:"wait",timeout:604800000,next:"negotiate",onError:"follow-up"},{id:"follow-up",name:"Follow Up",type:"action",next:"wait-response",retries:3},{id:"negotiate",name:"Negotiation",type:"action",next:"finalize"},{id:"finalize",name:"Finalize Deal",type:"decision",next:["won","lost"],condition:"deal.accepted"},{id:"won",name:"Deal Won",type:"end"},{id:"lost",name:"Deal Lost",type:"end"}]}],"saas-boilerplate":[{meta:{key:"user.onboarding",version:"1.0.0",description:"User onboarding workflow"},start:"signup",steps:[{id:"signup",name:"User Signup",type:"action",next:"verify-email"},{id:"verify-email",name:"Verify Email",type:"wait",timeout:86400000,next:"profile-setup",onError:"resend-verification"},{id:"resend-verification",name:"Resend Verification",type:"action",next:"verify-email",retries:2},{id:"profile-setup",name:"Setup Profile",type:"action",next:"onboarding-tour"},{id:"onboarding-tour",name:"Onboarding Tour",type:"action",next:"complete"},{id:"complete",name:"Onboarding Complete",type:"end"}]}],"agent-console":[{meta:{key:"agent.execution",version:"1.0.0",description:"Agent execution workflow"},start:"receive-task",steps:[{id:"receive-task",name:"Receive Task",type:"action",next:"plan-execution"},{id:"plan-execution",name:"Plan Execution",type:"action",next:"execute-steps"},{id:"execute-steps",name:"Execute Steps",type:"parallel",next:["tool-call","observe","reason"]},{id:"tool-call",name:"Tool Call",type:"action",next:"evaluate"},{id:"observe",name:"Observe",type:"action",next:"evaluate"},{id:"reason",name:"Reason",type:"action",next:"evaluate"},{id:"evaluate",name:"Evaluate Result",type:"decision",condition:"task.isComplete",next:["complete","execute-steps"]},{id:"complete",name:"Task Complete",type:"end"}]}],"todos-app":[{meta:{key:"task.lifecycle",version:"1.0.0",description:"Task lifecycle workflow"},start:"created",steps:[{id:"created",name:"Task Created",type:"action",next:"in-progress"},{id:"in-progress",name:"In Progress",type:"action",next:"review"},{id:"review",name:"Review",type:"decision",condition:"task.approved",next:["done","in-progress"]},{id:"done",name:"Done",type:"end"}]}],"messaging-app":[{meta:{key:"message.delivery",version:"1.0.0",description:"Message delivery workflow"},start:"compose",steps:[{id:"compose",name:"Compose Message",type:"action",next:"send"},{id:"send",name:"Send Message",type:"action",next:"deliver"},{id:"deliver",name:"Deliver",type:"decision",condition:"recipient.online",next:["delivered","queue"]},{id:"queue",name:"Queue for Delivery",type:"wait",next:"deliver"},{id:"delivered",name:"Message Delivered",type:"action",next:"read"},{id:"read",name:"Message Read",type:"end"}]}],"recipe-app-i18n":[{meta:{key:"recipe.creation",version:"1.0.0",description:"Recipe creation workflow"},start:"draft",steps:[{id:"draft",name:"Draft Recipe",type:"action",next:"add-ingredients"},{id:"add-ingredients",name:"Add Ingredients",type:"action",next:"add-steps"},{id:"add-steps",name:"Add Steps",type:"action",next:"review"},{id:"review",name:"Review Recipe",type:"decision",condition:"recipe.isComplete",next:["publish","draft"]},{id:"publish",name:"Publish Recipe",type:"end"}]}]}[q]??[]}export{rq as useWorkflowComposer,Sq as useSpecContent,cq as useRegistryTemplates,bq as useEvolution,Iq as useBehaviorTracking};
|