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

Function validateArtifactCode

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

Source from the content-addressed store, hash-verified

425
426/** `null` when the code may be rendered, otherwise the reason to hand back. */
427export const validateArtifactCode = (code: string): string | null => {
428 // Checked first: a five-segment address is the mistake a model that only read
429 // the `execute` skill will make, and every other rejection below would be a
430 // confusing thing to hear about code whose real problem is its addressing.
431 const oldStyleAddress = oldStyleAddressRejection(code);
432 if (oldStyleAddress) return oldStyleAddress;
433
434 if (REMOVED_RUN_CALL.test(code) && !LOCAL_RUN_DECLARATION.test(code)) {
435 return [
436 "`run(code)` no longer exists — artifact code is purely declarative `tools.*`.",
437 "Read data with useQuery(tools.<ns>.<tool>.queryOptions(args)),",
438 "page through a cursor with useInfiniteQuery(tools.<ns>.<tool>.infiniteQueryOptions(args, { getNextPageParam })),",
439 "and write with useMutation(tools.<ns>.<tool>.mutationOptions({ onSuccess })).",
440 ].join(" ");
441 }
442
443 const loopedHook = hookCalledInLoop(code);
444 if (loopedHook) {
445 return [
446 `\`${loopedHook}\` is called inside a loop body.`,
447 "React hooks must run unconditionally at the top level of the component, in the same order every render.",
448 PAGINATION_HOOKS.has(loopedHook)
449 ? "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."
450 : "Move the hook out of the loop, and derive per-item values from its result instead.",
451 ].join(" ");
452 }
453
454 const missingProvider = providerPairingRejection(code);
455 if (missingProvider) return missingProvider;
456
457 if (REACT_DESTRUCTURING_DECLARATION.test(code)) {
458 return [
459 "Do not destructure React in create-artifact.",
460 "Hooks such as useState are already in scope; use useState(...) directly or React.useState(...).",
461 ].join(" ");
462 }
463
464 for (const match of code.matchAll(OBJECT_DESTRUCTURING_DECLARATION)) {
465 const names = match[1]?.split(",").flatMap((part) => {
466 const name = localDestructuredName(part);
467 return name ? [name] : [];
468 });
469 const providedName = names?.find((name) => PROVIDED_GLOBAL_NAMES.has(name));
470 if (providedName) {
471 return [
472 `Provided global "${providedName}" is already in scope and cannot be redeclared.`,
473 "Remove the destructuring declaration and use the provided global directly.",
474 ].join(" ");
475 }
476 }
477
478 for (const match of code.matchAll(PROVIDED_GLOBAL_DECLARATION)) {
479 const name = firstDefined(match[1], match[2], match[3]);
480 if (name && PROVIDED_GLOBAL_NAMES.has(name)) {
481 return [
482 `Provided global "${name}" is already in scope and cannot be redeclared.`,
483 "Remove the local declaration and use the provided global directly.",
484 ].join(" ");

Callers 1

validateRenderAndSaveFunction · 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