@nodegx/mcp 0.2.7 → 0.2.8
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/README.md +6 -4
- package/dist/backend/cli.js +716 -495
- package/dist/cloud-bundle.cjs +98 -40
- package/dist/kit-extract.cjs +212 -9
- package/dist/kit-extract.inputs.json +1 -0
- package/dist/node-catalog.json +7 -7
- package/dist/noodl-mcp.cjs +3000 -1636
- package/dist/package.json +1 -1
- package/dist/render-harness/data/node-catalog-enriched.json +355 -16
- package/dist/render-harness/data/node-catalog.json +7 -7
- package/dist/render-harness/drive-steps.js +75 -19
- package/dist/src/external/viewer/noodl.viewer.js +1 -1
- package/package.json +1 -1
|
@@ -2964,6 +2964,62 @@ function matches(value, expected) {
|
|
|
2964
2964
|
}
|
|
2965
2965
|
return JSON.stringify(value) === JSON.stringify(expected);
|
|
2966
2966
|
}
|
|
2967
|
+
function watchShape(step, n) {
|
|
2968
|
+
const w = step.watch && typeof step.watch === "object" ? step.watch : {};
|
|
2969
|
+
if (w.look === void 0) throw new UsageError(`Step ${n}: watch takes { look: T } or { look: [T, T, \u2026] }.`);
|
|
2970
|
+
const unknown = Object.keys(w).filter((k) => !["look", "for_ms", "expect"].includes(k));
|
|
2971
|
+
if (unknown.length) throw new UsageError(`Step ${n}: watch does not take ${unknown.map((k) => JSON.stringify(k)).join(", ")} \u2014 it takes look, for_ms and expect.`);
|
|
2972
|
+
if (step.expect !== void 0 && w.expect !== void 0) throw new UsageError(`Step ${n}: give watch one expect, beside it or inside it, not both.`);
|
|
2973
|
+
const many = Array.isArray(w.look);
|
|
2974
|
+
const targets = many ? w.look : [w.look];
|
|
2975
|
+
if (targets.length === 0 || targets.length > 8) throw new UsageError(`Step ${n}: watch looks at 1 to 8 targets.`);
|
|
2976
|
+
return {
|
|
2977
|
+
many,
|
|
2978
|
+
targets,
|
|
2979
|
+
forMs: Math.min(1e4, Math.max(100, Number(step.for_ms ?? w.for_ms ?? 1500))),
|
|
2980
|
+
expect: step.expect !== void 0 ? step.expect : w.expect,
|
|
2981
|
+
expr: `Promise.all([${targets.map((t) => lookExpression(t)).join(", ")}])`
|
|
2982
|
+
};
|
|
2983
|
+
}
|
|
2984
|
+
function checkStep(step, n, { outDir } = {}) {
|
|
2985
|
+
const verb = verbOf(step, n);
|
|
2986
|
+
const at = (fn) => {
|
|
2987
|
+
try {
|
|
2988
|
+
return fn();
|
|
2989
|
+
} catch (error) {
|
|
2990
|
+
if (!error.usage) throw new UsageError(`Step ${n}: ${error instanceof Error ? error.message : String(error)}`);
|
|
2991
|
+
throw error.message.startsWith("Step ") ? error : new UsageError(`Step ${n}: ${error.message}`);
|
|
2992
|
+
}
|
|
2993
|
+
};
|
|
2994
|
+
if (verb === "tap") at(() => finder(step.tap));
|
|
2995
|
+
else if (verb === "type") {
|
|
2996
|
+
if (typeof step.type !== "string") throw new UsageError(`Step ${n}: type takes a string.`);
|
|
2997
|
+
if (step.into !== void 0) at(() => finder(step.into));
|
|
2998
|
+
} else if (verb === "key") {
|
|
2999
|
+
if (!KEYS[step.key]) throw new UsageError(`Step ${n}: key "${step.key}" is not one of ${Object.keys(KEYS).join(", ")}.`);
|
|
3000
|
+
} else if (verb === "wait_for") at(() => waitExpression(step.wait_for));
|
|
3001
|
+
else if (verb === "read") {
|
|
3002
|
+
const r = step.read && typeof step.read === "object" ? step.read : null;
|
|
3003
|
+
if (!r || !RUN_READS.some((k) => r[k] !== void 0)) at(() => readExpression(step.read));
|
|
3004
|
+
} else if (verb === "navigate") {
|
|
3005
|
+
if (typeof step.navigate !== "string") throw new UsageError(`Step ${n}: navigate takes a route string.`);
|
|
3006
|
+
} else if (verb === "watch") watchShape(step, n);
|
|
3007
|
+
else if (verb === "viewport") at(() => parseViewports(String(step.viewport)));
|
|
3008
|
+
else if (verb === "screenshot") {
|
|
3009
|
+
if (!outDir) throw new UsageError(`Step ${n}: screenshot needs --out-dir (render_report's out_dir rule).`);
|
|
3010
|
+
} else if (verb === "seed_storage") {
|
|
3011
|
+
const seed = step.seed_storage;
|
|
3012
|
+
if (typeof seed === "string") {
|
|
3013
|
+
if (typeof step.edit !== "string") throw new UsageError(`Step ${n}: seed_storage "${seed}" edits a saved value and needs edit: "js that changes v".`);
|
|
3014
|
+
} else if (!seed || typeof seed !== "object" || Array.isArray(seed)) {
|
|
3015
|
+
throw new UsageError(`Step ${n}: seed_storage takes { key: value } or a key with edit: "js that changes v".`);
|
|
3016
|
+
}
|
|
3017
|
+
} else if (verb === "stub_route") parseStub(step, n);
|
|
3018
|
+
else if (verb === "reduced_motion") {
|
|
3019
|
+
if (typeof step.reduced_motion !== "boolean") throw new UsageError(`Step ${n}: reduced_motion takes true or false.`);
|
|
3020
|
+
}
|
|
3021
|
+
return verb;
|
|
3022
|
+
}
|
|
2967
3023
|
async function runSteps(page, steps, { outDir, stubs = routeStubs(page), network, beforeBoot = 0 }) {
|
|
2968
3024
|
const rows = [];
|
|
2969
3025
|
const pendingSeeds = [];
|
|
@@ -3089,17 +3145,7 @@ async function runSteps(page, steps, { outDir, stubs = routeStubs(page), network
|
|
|
3089
3145
|
}
|
|
3090
3146
|
row.url = await evaluate("location.pathname + location.hash");
|
|
3091
3147
|
} else if (verb === "watch") {
|
|
3092
|
-
const
|
|
3093
|
-
if (w.look === void 0) throw new UsageError(`Step ${i + 1}: watch takes { look: T } or { look: [T, T, \u2026] }.`);
|
|
3094
|
-
const unknown = Object.keys(w).filter((k) => !["look", "for_ms", "expect"].includes(k));
|
|
3095
|
-
if (unknown.length) throw new UsageError(`Step ${i + 1}: watch does not take ${unknown.map((k) => JSON.stringify(k)).join(", ")} \u2014 it takes look, for_ms and expect.`);
|
|
3096
|
-
if (step.expect !== void 0 && w.expect !== void 0) throw new UsageError(`Step ${i + 1}: give watch one expect, beside it or inside it, not both.`);
|
|
3097
|
-
const forMs = Math.min(1e4, Math.max(100, Number(step.for_ms ?? w.for_ms ?? 1500)));
|
|
3098
|
-
const expect = step.expect !== void 0 ? step.expect : w.expect;
|
|
3099
|
-
const many = Array.isArray(w.look);
|
|
3100
|
-
const targets = many ? w.look : [w.look];
|
|
3101
|
-
if (targets.length === 0 || targets.length > 8) throw new UsageError(`Step ${i + 1}: watch looks at 1 to 8 targets.`);
|
|
3102
|
-
const expr = `Promise.all([${targets.map((t) => lookExpression(t)).join(", ")}])`;
|
|
3148
|
+
const { many, targets, forMs, expect, expr } = watchShape(step, i + 1);
|
|
3103
3149
|
const keyOf = (v) => v && typeof v === "object" ? JSON.stringify({ text: v.text, visible: v.visible, backgroundName: v.backgroundName, colorName: v.colorName, opacity: v.opacity === void 0 ? void 0 : Math.round(v.opacity * 10) / 10 }) : "null";
|
|
3104
3150
|
const states = targets.map(() => []);
|
|
3105
3151
|
const t0 = Date.now();
|
|
@@ -3182,8 +3228,8 @@ async function main() {
|
|
|
3182
3228
|
return i === -1 ? void 0 : argv[i + 1];
|
|
3183
3229
|
};
|
|
3184
3230
|
const projectDir = argv.find((a, i) => !a.startsWith("--") && !VALUE_FLAGS.has(argv[i - 1]));
|
|
3185
|
-
const usage = (message) => {
|
|
3186
|
-
process.stdout.write(JSON.stringify({ error: { message, problems:
|
|
3231
|
+
const usage = (message, problems2 = [message]) => {
|
|
3232
|
+
process.stdout.write(JSON.stringify({ error: { message, problems: problems2, actionable: true } }));
|
|
3187
3233
|
process.exit(2);
|
|
3188
3234
|
};
|
|
3189
3235
|
if (!projectDir) usage("drive-steps.js <project-dir> --steps <json|@file> [--page p] [--viewport v] [--out-dir d]");
|
|
@@ -3193,12 +3239,22 @@ async function main() {
|
|
|
3193
3239
|
if (!raw) throw new UsageError("--steps is required: a JSON array of steps.");
|
|
3194
3240
|
steps = JSON.parse(raw.startsWith("@") ? fs.readFileSync(raw.slice(1), "utf8") : raw);
|
|
3195
3241
|
if (!Array.isArray(steps) || steps.length === 0) throw new UsageError("--steps must be a non-empty JSON array.");
|
|
3196
|
-
steps.forEach((s, i) => {
|
|
3197
|
-
if (verbOf(s, i + 1) === "stub_route") parseStub(s, i + 1);
|
|
3198
|
-
});
|
|
3199
3242
|
} catch (error) {
|
|
3200
3243
|
usage(error.message);
|
|
3201
3244
|
}
|
|
3245
|
+
const problems = [];
|
|
3246
|
+
steps.forEach((step, i) => {
|
|
3247
|
+
try {
|
|
3248
|
+
checkStep(step, i + 1, { outDir: flag("--out-dir") });
|
|
3249
|
+
} catch (error) {
|
|
3250
|
+
if (!error.usage) throw error;
|
|
3251
|
+
problems.push(error.message);
|
|
3252
|
+
}
|
|
3253
|
+
});
|
|
3254
|
+
if (problems.length) {
|
|
3255
|
+
problems.push("No step ran: every step is checked before the first one does.");
|
|
3256
|
+
usage(problems.join(" "), problems);
|
|
3257
|
+
}
|
|
3202
3258
|
let viewport;
|
|
3203
3259
|
try {
|
|
3204
3260
|
[viewport] = parseViewports(flag("--viewport") || "desktop");
|
|
@@ -3250,9 +3306,9 @@ async function main() {
|
|
|
3250
3306
|
process.stdout.write(JSON.stringify(result));
|
|
3251
3307
|
} catch (error) {
|
|
3252
3308
|
if (error.usage) usage(error.message);
|
|
3253
|
-
const
|
|
3254
|
-
process.stdout.write(JSON.stringify({ error: { message:
|
|
3309
|
+
const problems2 = error.problems || [error instanceof Error ? error.message : String(error)];
|
|
3310
|
+
process.stdout.write(JSON.stringify({ error: { message: problems2.join(" "), problems: problems2, actionable: Boolean(error.actionable) } }));
|
|
3255
3311
|
}
|
|
3256
3312
|
}
|
|
3257
3313
|
if (require.main === module) main();
|
|
3258
|
-
module.exports = { runSteps, locateExpression, routeStubs, networkLog, parseStub, VERBS };
|
|
3314
|
+
module.exports = { runSteps, checkStep, locateExpression, routeStubs, networkLog, parseStub, VERBS };
|