MCPcopy Create free account
hub / github.com/UsefulSoftwareCo/executor / validateArtifactCode

Function validateArtifactCode

packages/hosts/mcp/src/create-artifact.ts:345–407  ·  view source on GitHub ↗
(code: string)

Source from the content-addressed store, hash-verified

343
344/** `null` when the code may be rendered, otherwise the reason to hand back. */
345export const validateArtifactCode = (code: string): string | null => {
346 // Checked first: a five-segment address is the mistake a model that only read
347 // the `execute` skill will make, and every other rejection below would be a
348 // confusing thing to hear about code whose real problem is its addressing.
349 const oldStyleAddress = oldStyleAddressRejection(code);
350 if (oldStyleAddress) return oldStyleAddress;
351
352 if (REMOVED_RUN_CALL.test(code) && !LOCAL_RUN_DECLARATION.test(code)) {
353 return [
354 "`run(code)` no longer exists — artifact code is purely declarative `tools.*`.",
355 "Read data with useQuery(tools.<ns>.<tool>.queryOptions(args)),",
356 "page through a cursor with useInfiniteQuery(tools.<ns>.<tool>.infiniteQueryOptions(args, { getNextPageParam })),",
357 "and write with useMutation(tools.<ns>.<tool>.mutationOptions({ onSuccess })).",
358 ].join(" ");
359 }
360
361 const loopedHook = hookCalledInLoop(code);
362 if (loopedHook) {
363 return [
364 `\`${loopedHook}\` is called inside a loop body.`,
365 "React hooks must run unconditionally at the top level of the component, in the same order every render.",
366 PAGINATION_HOOKS.has(loopedHook)
367 ? "To read every page of a cursor, use one useInfiniteQuery(tools.<ns>.<tool>.infiniteQueryOptions(args, { cursorKey, getNextPageParam })) and render data.pages — never a loop of useQuery calls, one per page."
368 : "Move the hook out of the loop, and derive per-item values from its result instead.",
369 ].join(" ");
370 }
371
372 const missingProvider = providerPairingRejection(code);
373 if (missingProvider) return missingProvider;
374
375 if (REACT_DESTRUCTURING_DECLARATION.test(code)) {
376 return [
377 "Do not destructure React in create-artifact.",
378 "Hooks such as useState are already in scope; use useState(...) directly or React.useState(...).",
379 ].join(" ");
380 }
381
382 for (const match of code.matchAll(OBJECT_DESTRUCTURING_DECLARATION)) {
383 const names = match[1]?.split(",").flatMap((part) => {
384 const name = localDestructuredName(part);
385 return name ? [name] : [];
386 });
387 const providedName = names?.find((name) => PROVIDED_GLOBAL_NAMES.has(name));
388 if (providedName) {
389 return [
390 `Provided global "${providedName}" is already in scope and cannot be redeclared.`,
391 "Remove the destructuring declaration and use the provided global directly.",
392 ].join(" ");
393 }
394 }
395
396 for (const match of code.matchAll(PROVIDED_GLOBAL_DECLARATION)) {
397 const name = firstDefined(match[1], match[2], match[3]);
398 if (name && PROVIDED_GLOBAL_NAMES.has(name)) {
399 return [
400 `Provided global "${name}" is already in scope and cannot be redeclared.`,
401 "Remove the local declaration and use the provided global directly.",
402 ].join(" ");

Callers 1

createArtifactFunction · 0.90

Calls 5

oldStyleAddressRejectionFunction · 0.90
hookCalledInLoopFunction · 0.85
providerPairingRejectionFunction · 0.85
localDestructuredNameFunction · 0.85
firstDefinedFunction · 0.85

Tested by

no test coverage detected