@plitzi/sdk-mcp 0.37.2 → 0.37.3
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/CHANGELOG.md +1156 -0
- package/README.md +6 -2
- package/dist/createServer.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -1
- package/dist/modules/mcp/accessibilityOutline.d.ts +38 -0
- package/dist/modules/mcp/accessibilityOutline.js +75 -0
- package/dist/modules/mcp/accessibilityOutline.test.d.ts +1 -0
- package/dist/modules/mcp/catalogs/index.d.ts +2 -2
- package/dist/modules/mcp/catalogs/index.js +2 -2
- package/dist/modules/mcp/catalogs/observed.js +2 -2
- package/dist/modules/mcp/catalogs/pluginCatalog.d.ts +6 -3
- package/dist/modules/mcp/catalogs/pluginCatalog.js +12 -7
- package/dist/modules/mcp/helpers/agentPrompt.js +22 -5
- package/dist/modules/mcp/helpers/guide.d.ts +2 -2
- package/dist/modules/mcp/helpers/guide.js +212 -48
- package/dist/modules/mcp/helpers/interactions.d.ts +2 -1
- package/dist/modules/mcp/helpers/interactions.js +2 -0
- package/dist/modules/mcp/helpers/space.d.ts +6 -4
- package/dist/modules/mcp/helpers/space.js +7 -20
- package/dist/modules/mcp/helpers/uris.d.ts +3 -1
- package/dist/modules/mcp/helpers/uris.js +4 -1
- package/dist/modules/mcp/index.d.ts +2 -0
- package/dist/modules/mcp/localScreenshotClient.d.ts +11 -0
- package/dist/modules/mcp/localScreenshotClient.js +24 -9
- package/dist/modules/mcp/proxy/guard.d.ts +6 -0
- package/dist/modules/mcp/proxy/guard.js +8 -12
- package/dist/modules/mcp/resources/functions.d.ts +8 -0
- package/dist/modules/mcp/resources/functions.js +35 -0
- package/dist/modules/mcp/resources/register.d.ts +2 -1
- package/dist/modules/mcp/resources/register.js +49 -2
- package/dist/modules/mcp/resources/renderGuide.js +3 -2
- package/dist/modules/mcp/resources/router.js +3 -1
- package/dist/modules/mcp/screenshotClient.js +12 -4
- package/dist/modules/mcp/server.js +20 -10
- package/dist/modules/mcp/tests/guide.test.d.ts +1 -0
- package/dist/modules/mcp/tools/apply/dispatch.js +9 -1
- package/dist/modules/mcp/tools/apply/index.d.ts +34 -0
- package/dist/modules/mcp/tools/apply/index.js +35 -70
- package/dist/modules/mcp/tools/index.js +4 -2
- package/dist/modules/mcp/tools/operations/connectors/manifest.d.ts +0 -1
- package/dist/modules/mcp/tools/operations/functions/functions.test.d.ts +1 -0
- package/dist/modules/mcp/tools/operations/functions/index.d.ts +31 -0
- package/dist/modules/mcp/tools/operations/functions/index.js +48 -0
- package/dist/modules/mcp/tools/operations/index.d.ts +122 -2
- package/dist/modules/mcp/tools/operations/index.js +4 -3
- package/dist/modules/mcp/tools/operations/schema/elements/deleteElement.js +5 -6
- package/dist/modules/mcp/tools/operations/schema/elements/moveElement.js +3 -11
- package/dist/modules/mcp/tools/operations/schema/elements/upsertElement.js +6 -9
- package/dist/modules/mcp/tools/operations/schema/interactions/patchInteractionNode.d.ts +5 -0
- package/dist/modules/mcp/tools/operations/schema/interactions/patchInteractionNode.js +3 -1
- package/dist/modules/mcp/tools/operations/schema/interactions/upsertInteractionFlow.d.ts +5 -0
- package/dist/modules/mcp/tools/operations/schema/operations.d.ts +27 -0
- package/dist/modules/mcp/tools/operations/schema/pages/deleteLayout.js +6 -5
- package/dist/modules/mcp/tools/operations/schema/pages/deletePage.js +6 -5
- package/dist/modules/mcp/tools/operations/schema/settings/patchSettings.d.ts +17 -0
- package/dist/modules/mcp/tools/operations/schema/settings/patchSettings.js +34 -5
- package/dist/modules/mcp/tools/operations/schema/shared.d.ts +6 -0
- package/dist/modules/mcp/tools/operations/schema/shared.js +5 -0
- package/dist/modules/mcp/tools/operations/schema/translator.js +3 -2
- package/dist/modules/mcp/tools/operations/schema/write.d.ts +10 -5
- package/dist/modules/mcp/tools/operations/schema/write.js +16 -16
- package/dist/modules/mcp/tools/preview.d.ts +27 -0
- package/dist/modules/mcp/tools/render.d.ts +27 -0
- package/dist/modules/mcp/tools/render.js +7 -43
- package/dist/modules/mcp/tools/screenshot.d.ts +39 -0
- package/dist/modules/mcp/tools/screenshot.js +40 -5
- package/dist/modules/mcp/tools/shared/draftBatch.d.ts +25 -0
- package/dist/modules/mcp/tools/shared/draftBatch.js +58 -0
- package/dist/modules/mcp/tools/shared/draftBatch.test.d.ts +1 -0
- package/dist/modules/mcp/tools/shared/lintDraft.d.ts +30 -0
- package/dist/modules/mcp/tools/shared/lintDraft.js +142 -0
- package/dist/modules/mcp/tools/shared/lintDraft.test.d.ts +1 -0
- package/dist/modules/mcp/tools/shared/tool.d.ts +4 -0
- package/dist/modules/mcp/tools/shared/validator/bindings.d.ts +0 -4
- package/dist/modules/mcp/tools/shared/validator/bindings.js +1 -60
- package/dist/modules/mcp/tools/shared/validator/elements.js +3 -15
- package/dist/modules/mcp/tools/shared/validator/index.d.ts +1 -4
- package/dist/modules/mcp/tools/shared/validator/index.js +25 -20
- package/dist/modules/mcp/tools/shared/validator/interactions.js +3 -58
- package/dist/modules/mcp/tools/tryFunction.d.ts +18 -0
- package/dist/modules/mcp/tools/tryFunction.js +43 -0
- package/dist/modules/mcp/tools/validate.d.ts +34 -0
- package/dist/modules/mcp/tools/validate.js +9 -26
- package/dist/modules/mcp/types/aiSchema.d.ts +8 -1
- package/dist/modules/mcp/types/previewTypes.d.ts +6 -0
- package/dist/modules/mcp/types/screenshotTypes.d.ts +16 -0
- package/dist/modules/mcp/types/toolTypes.d.ts +5 -1
- package/dist/preview/createPreview.js +5 -4
- package/dist/stages/preview.js +15 -1
- package/package.json +11 -10
- package/skills/plitzi-render/SKILL.md +1 -2
- package/dist/modules/mcp/tools/shared/validator/audit.d.ts +0 -4
- package/dist/modules/mcp/tools/shared/validator/audit.js +0 -127
|
@@ -1,34 +1,14 @@
|
|
|
1
|
-
import { getElementCallback, getGlobalCallback, getUtility, hiddenParams,
|
|
1
|
+
import { getElementCallback, getGlobalCallback, getUtility, hiddenParams, reconcileParams } from "../../../catalogs/index.js";
|
|
2
2
|
import { NULLISH_ELEMENT_IDS } from "../../../helpers/interactions.js";
|
|
3
3
|
import { checkObservedName, warnOnce } from "./context.js";
|
|
4
4
|
//#region src/modules/mcp/tools/shared/validator/interactions.ts
|
|
5
5
|
var ELEMENT_FIELD_ACTIONS = /* @__PURE__ */ new Set(["setState", "toggleState"]);
|
|
6
|
-
var describeType = (type) => {
|
|
7
|
-
switch (type) {
|
|
8
|
-
case "boolean": return "a boolean";
|
|
9
|
-
case "number": return "a number";
|
|
10
|
-
case "select": return "one of its allowed values";
|
|
11
|
-
case "scalar": return "a string, number or boolean";
|
|
12
|
-
default: return "a string";
|
|
13
|
-
}
|
|
14
|
-
};
|
|
15
6
|
var checkParams = (label, action, params, spec, base, ctx) => {
|
|
16
7
|
const unknown = Object.keys(params).filter((key) => !(key in spec));
|
|
17
8
|
if (unknown.length > 0) warnOnce(ctx, `${label} "${action}" at ${base} got unknown param(s) ${unknown.map((k) => `"${k}"`).join(", ")} — these are dropped on apply. Valid params: ${Object.keys(spec).join(", ")}.`);
|
|
18
9
|
const effective = reconcileParams(params, spec, true);
|
|
19
|
-
const missing = missingRequiredParams(params, effective, spec);
|
|
20
|
-
if (missing.length > 0) warnOnce(ctx, `${label} "${action}" at ${base} is missing required param(s) ${missing.map((k) => `"${k}"`).join(", ")} — the step is malformed without them (see plitzi://interactions for what each does).`);
|
|
21
10
|
const hidden = hiddenParams(params, effective, spec);
|
|
22
11
|
if (hidden.length > 0) warnOnce(ctx, `${label} "${action}" at ${base}: param(s) ${hidden.map((k) => `"${k}"`).join(", ")} are ignored under the current params (they only apply when a companion param is set — see plitzi://interactions). Set the companion param or drop them.`);
|
|
23
|
-
for (const { key, expected, got, options } of invalidParams(params, effective, spec)) {
|
|
24
|
-
const detail = options ? ` Allowed values: ${options.join(", ")}.` : "";
|
|
25
|
-
ctx.errors.push({
|
|
26
|
-
path: `${base}.params.${key}`,
|
|
27
|
-
message: `${label} "${action}" at ${base}: param "${key}" must be ${describeType(expected)} but got ${got}.${detail}`,
|
|
28
|
-
hint: `Set "${key}" to ${describeType(expected)}.`,
|
|
29
|
-
...options ? { validValues: options } : {}
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
12
|
};
|
|
33
13
|
var checkGlobalCallback = (node, base, ctx) => {
|
|
34
14
|
const builtin = getGlobalCallback(node.action);
|
|
@@ -46,50 +26,15 @@ var checkElementFieldKey = (node, base, ctx, hostRef) => {
|
|
|
46
26
|
const targetRef = typeof node.elementId === "string" && node.elementId !== "" ? node.elementId : hostRef;
|
|
47
27
|
const type = ctx.elementType(targetRef);
|
|
48
28
|
const meta = type ? ctx.typeMeta.get(type) : void 0;
|
|
49
|
-
if (!meta) return;
|
|
29
|
+
if (!meta?.custom) return;
|
|
50
30
|
const state = node.params?.category === "state";
|
|
51
31
|
const validKeys = state ? /* @__PURE__ */ new Set(["visibility", ...[...meta.styleSelectors].map((selector) => `styleSelectors.${selector}`)]) : meta.attributes;
|
|
52
32
|
if (validKeys.size === 0 || validKeys.has(key)) return;
|
|
53
33
|
const kind = state ? "state" : "attribute";
|
|
54
34
|
const valid = [...validKeys].sort();
|
|
55
|
-
|
|
56
|
-
if (!meta.custom) ctx.errors.push({
|
|
57
|
-
path: base,
|
|
58
|
-
message: detail,
|
|
59
|
-
hint: `Use one of: ${valid.join(", ")}`,
|
|
60
|
-
validValues: valid
|
|
61
|
-
});
|
|
62
|
-
else warnOnce(ctx, `${detail} (${valid.join(", ")}). It may still be valid — verify against plitzi://types.`);
|
|
63
|
-
};
|
|
64
|
-
/**
|
|
65
|
-
* The step names another element to act on — and that element does not exist.
|
|
66
|
-
*
|
|
67
|
-
* This is the failure with NO symptom: the runtime resolves a callback as `callbacksAvailables[elementId][action]`,
|
|
68
|
-
* so a dangling target finds nothing and the step does nothing, while the flow, the render and the apply all
|
|
69
|
-
* report success. Nothing else in the pipeline catches it — the field-key check below needs the element's type to
|
|
70
|
-
* say anything, so an unresolvable ref used to make it return in silence.
|
|
71
|
-
*
|
|
72
|
-
* A `widget` batch is the whole world (plitzi_render seeds an empty space), so a target that is neither in it nor
|
|
73
|
-
* created by it is certainly wrong: a hard error. A space may carry legacy flows pointing at elements deleted long
|
|
74
|
-
* ago, and the post-apply audit re-checks the stored interactions of every element the batch touches — erroring
|
|
75
|
-
* there would block edits on debris the agent did not write, so in `space` mode this warns.
|
|
76
|
-
*/
|
|
77
|
-
var checkCallbackTarget = (node, base, ctx) => {
|
|
78
|
-
const target = node.elementId;
|
|
79
|
-
if (target === void 0 || target === "" || NULLISH_ELEMENT_IDS.has(target) || ctx.elementExists(target)) return;
|
|
80
|
-
const detail = `Element callback "${node.action}" at ${base} targets elementId "${target}", which is not an element in this widget`;
|
|
81
|
-
if (ctx.mode === "widget") {
|
|
82
|
-
ctx.errors.push({
|
|
83
|
-
path: `${base}.elementId`,
|
|
84
|
-
message: detail,
|
|
85
|
-
hint: "The runtime looks the callback up on that element, so the step silently does nothing. Use the ref of an element the batch authors, or omit elementId to act on the flow host."
|
|
86
|
-
});
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
warnOnce(ctx, `Element callback "${node.action}" at ${base} targets elementId "${target}", which is not an element in this space — the step resolves to nothing and does nothing. Point it at a real element ref, or omit elementId to act on the flow host.`);
|
|
35
|
+
warnOnce(ctx, `Element ${node.action} at ${base} names ${kind} "${key}" on type "${type}", which has no such ${kind} key (${valid.join(", ")}). It may still be valid — verify against plitzi://types.`);
|
|
90
36
|
};
|
|
91
37
|
var checkElementCallback = (node, base, ctx, hostRef) => {
|
|
92
|
-
checkCallbackTarget(node, base, ctx);
|
|
93
38
|
const builtin = getElementCallback(node.action);
|
|
94
39
|
if (!builtin) {
|
|
95
40
|
if (getUtility(node.action)) warnOnce(ctx, `"${node.action}" at ${base} is a utility, not an element callback — use nodeType "utility".`);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ToolContext } from './shared/tool';
|
|
3
|
+
export declare const tryFunctionShape: {
|
|
4
|
+
task: z.ZodString;
|
|
5
|
+
params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
6
|
+
dryRun: z.ZodOptional<z.ZodBoolean>;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Runs one task of the space's functions — the draft, in the sandbox, with the limits and the record any run has — and
|
|
10
|
+
* answers what it returned, what it logged and where it failed. How an agent checks the code it wrote before a page
|
|
11
|
+
* relies on it: the live site only runs what the space was last published with.
|
|
12
|
+
*/
|
|
13
|
+
export declare const tryFunction: (input: {
|
|
14
|
+
task: string;
|
|
15
|
+
params?: Record<string, unknown>;
|
|
16
|
+
dryRun?: boolean;
|
|
17
|
+
}, ctx: ToolContext) => Promise<unknown>;
|
|
18
|
+
export declare const tryFunctionTool: import('.').ToolDef;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { defineTool } from "./shared/tool.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/modules/mcp/tools/tryFunction.ts
|
|
4
|
+
var tryFunctionShape = {
|
|
5
|
+
task: z.string().describe("`<namespace>.<action>`"),
|
|
6
|
+
params: z.record(z.string(), z.unknown()).optional(),
|
|
7
|
+
dryRun: z.boolean().optional()
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Runs one task of the space's functions — the draft, in the sandbox, with the limits and the record any run has — and
|
|
11
|
+
* answers what it returned, what it logged and where it failed. How an agent checks the code it wrote before a page
|
|
12
|
+
* relies on it: the live site only runs what the space was last published with.
|
|
13
|
+
*/
|
|
14
|
+
var tryFunction = async (input, ctx) => {
|
|
15
|
+
if (input.dryRun) return {
|
|
16
|
+
ran: false,
|
|
17
|
+
reason: "Trying a function runs its code for real — its fetches and its writes — so not in plan mode"
|
|
18
|
+
};
|
|
19
|
+
if (!ctx.tryFunction) return {
|
|
20
|
+
ran: false,
|
|
21
|
+
reason: "This deployment runs no space functions"
|
|
22
|
+
};
|
|
23
|
+
const report = await ctx.tryFunction(input.task, input.params ?? {});
|
|
24
|
+
const step = report.steps.find((entry) => entry.action === input.task);
|
|
25
|
+
return {
|
|
26
|
+
ran: true,
|
|
27
|
+
status: report.status,
|
|
28
|
+
value: report.output.value,
|
|
29
|
+
...step?.error ? { error: step.error } : {},
|
|
30
|
+
...step?.logs ? { logs: step.logs } : {},
|
|
31
|
+
...step ? { ms: step.endTime - step.startTime } : {}
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
var tryFunctionTool = defineTool({
|
|
35
|
+
name: "plitzi_try_function",
|
|
36
|
+
title: "Try function",
|
|
37
|
+
description: "Run one task of the space’s functions against the draft, in the sandbox: its value, logs and error. Its writes are real.",
|
|
38
|
+
inputShape: tryFunctionShape,
|
|
39
|
+
access: "write",
|
|
40
|
+
run: (input, ctx) => tryFunction(input, ctx)
|
|
41
|
+
});
|
|
42
|
+
//#endregion
|
|
43
|
+
export { tryFunction, tryFunctionShape, tryFunctionTool };
|
|
@@ -188,6 +188,11 @@ export declare const validateShape: {
|
|
|
188
188
|
params: import('zod').ZodOptional<import('zod').ZodRecord<import('zod').ZodString, import('zod').ZodUnknown>>;
|
|
189
189
|
enabled: import('zod').ZodOptional<import('zod').ZodBoolean>;
|
|
190
190
|
when: import('zod').ZodOptional<import('zod').ZodType<import('@plitzi/sdk-shared').RuleGroup, unknown, import('zod/v4/core').$ZodTypeInternals<import('@plitzi/sdk-shared').RuleGroup, unknown>>>;
|
|
191
|
+
whileRunning: import('zod').ZodOptional<import('zod').ZodEnum<{
|
|
192
|
+
skip: "skip";
|
|
193
|
+
parallel: "parallel";
|
|
194
|
+
queue: "queue";
|
|
195
|
+
}>>;
|
|
191
196
|
elementId: import('zod').ZodOptional<import('zod').ZodString>;
|
|
192
197
|
preview: import('zod').ZodOptional<import('zod').ZodRecord<import('zod').ZodString, import('zod').ZodUnknown>>;
|
|
193
198
|
}, import('zod/v4/core').$strip>>;
|
|
@@ -201,6 +206,11 @@ export declare const validateShape: {
|
|
|
201
206
|
params: import('zod').ZodOptional<import('zod').ZodRecord<import('zod').ZodString, import('zod').ZodUnknown>>;
|
|
202
207
|
enabled: import('zod').ZodOptional<import('zod').ZodBoolean>;
|
|
203
208
|
when: import('zod').ZodOptional<import('zod').ZodType<import('@plitzi/sdk-shared').RuleGroup, unknown, import('zod/v4/core').$ZodTypeInternals<import('@plitzi/sdk-shared').RuleGroup, unknown>>>;
|
|
209
|
+
whileRunning: import('zod').ZodOptional<import('zod').ZodEnum<{
|
|
210
|
+
skip: "skip";
|
|
211
|
+
parallel: "parallel";
|
|
212
|
+
queue: "queue";
|
|
213
|
+
}>>;
|
|
204
214
|
elementId: import('zod').ZodOptional<import('zod').ZodString>;
|
|
205
215
|
preview: import('zod').ZodOptional<import('zod').ZodRecord<import('zod').ZodString, import('zod').ZodUnknown>>;
|
|
206
216
|
}, import('zod/v4/core').$strip>, import('zod').ZodObject<{
|
|
@@ -217,6 +227,8 @@ export declare const validateShape: {
|
|
|
217
227
|
localStorage: "localStorage";
|
|
218
228
|
sessionStorage: "sessionStorage";
|
|
219
229
|
}>>;
|
|
230
|
+
transientState: import('zod').ZodOptional<import('zod').ZodArray<import('zod').ZodString>>;
|
|
231
|
+
paintedState: import('zod').ZodOptional<import('zod').ZodArray<import('zod').ZodString>>;
|
|
220
232
|
userProvider: import('zod').ZodOptional<import('zod').ZodString>;
|
|
221
233
|
tokenStorage: import('zod').ZodOptional<import('zod').ZodEnum<{
|
|
222
234
|
"": "";
|
|
@@ -239,6 +251,21 @@ export declare const validateShape: {
|
|
|
239
251
|
strict: "strict";
|
|
240
252
|
}>>;
|
|
241
253
|
sessionRevalidateSeconds: import('zod').ZodOptional<import('zod').ZodNumber>;
|
|
254
|
+
channels: import('zod').ZodOptional<import('zod').ZodRecord<import('zod').ZodString, import('zod').ZodNullable<import('zod').ZodObject<{
|
|
255
|
+
access: import('zod').ZodObject<{
|
|
256
|
+
mode: import('zod').ZodEnum<{
|
|
257
|
+
public: "public";
|
|
258
|
+
session: "session";
|
|
259
|
+
role: "role";
|
|
260
|
+
}>;
|
|
261
|
+
permissions: import('zod').ZodOptional<import('zod').ZodArray<import('zod').ZodString>>;
|
|
262
|
+
}, import('zod/v4/core').$strip>;
|
|
263
|
+
publish: import('zod').ZodOptional<import('zod').ZodEnum<{
|
|
264
|
+
server: "server";
|
|
265
|
+
clients: "clients";
|
|
266
|
+
}>>;
|
|
267
|
+
presence: import('zod').ZodOptional<import('zod').ZodBoolean>;
|
|
268
|
+
}, import('zod/v4/core').$strip>>>>;
|
|
242
269
|
}, import('zod/v4/core').$strip>, import('zod').ZodObject<{
|
|
243
270
|
desktop: import('zod').ZodOptional<import('zod').ZodRecord<import('zod').ZodString, import('zod').ZodUnion<readonly [import('zod').ZodString, import('zod').ZodNumber]>>>;
|
|
244
271
|
tablet: import('zod').ZodOptional<import('zod').ZodRecord<import('zod').ZodString, import('zod').ZodUnion<readonly [import('zod').ZodString, import('zod').ZodNumber]>>>;
|
|
@@ -991,6 +1018,13 @@ export declare const validateShape: {
|
|
|
991
1018
|
}, import('zod/v4/core').$strip>, import('zod').ZodObject<{
|
|
992
1019
|
type: import('zod').ZodLiteral<"deleteAction">;
|
|
993
1020
|
ref: import('zod').ZodString;
|
|
1021
|
+
}, import('zod/v4/core').$strip>, import('zod').ZodObject<{
|
|
1022
|
+
type: import('zod').ZodLiteral<"upsertFunctionFile">;
|
|
1023
|
+
path: import('zod').ZodString;
|
|
1024
|
+
content: import('zod').ZodString;
|
|
1025
|
+
}, import('zod/v4/core').$strip>, import('zod').ZodObject<{
|
|
1026
|
+
type: import('zod').ZodLiteral<"deleteFunctionFile">;
|
|
1027
|
+
path: import('zod').ZodString;
|
|
994
1028
|
}, import('zod/v4/core').$strip>], "type">>;
|
|
995
1029
|
};
|
|
996
1030
|
export declare const validate: (input: ValidateInput, space: Space) => ValidationResult;
|
|
@@ -1,38 +1,21 @@
|
|
|
1
|
-
import { cloneSpace } from "../helpers/space.js";
|
|
2
1
|
import { environment, operations } from "./operations/index.js";
|
|
3
|
-
import {
|
|
4
|
-
import { expandOperations } from "./shared/expandOperations.js";
|
|
2
|
+
import { draftBatch } from "./shared/draftBatch.js";
|
|
5
3
|
import { defineTool } from "./shared/tool.js";
|
|
6
|
-
import { validateOperations } from "./shared/validator/index.js";
|
|
7
|
-
import { auditResources } from "./shared/validator/audit.js";
|
|
8
4
|
//#region src/modules/mcp/tools/validate.ts
|
|
9
5
|
var validateShape = {
|
|
10
6
|
environment,
|
|
11
7
|
operations
|
|
12
8
|
};
|
|
13
9
|
var validate = (input, space) => {
|
|
14
|
-
const
|
|
15
|
-
|
|
10
|
+
const result = draftBatch(space, input.environment ?? "main", input.operations);
|
|
11
|
+
return result.ok ? {
|
|
12
|
+
valid: true,
|
|
13
|
+
errors: [],
|
|
14
|
+
warnings: result.warnings
|
|
15
|
+
} : {
|
|
16
16
|
valid: false,
|
|
17
|
-
errors:
|
|
18
|
-
warnings:
|
|
19
|
-
};
|
|
20
|
-
const ops = expansion.operations;
|
|
21
|
-
const validation = validateOperations(space, ops);
|
|
22
|
-
if (!validation.valid) return validation;
|
|
23
|
-
const env = input.environment ?? "main";
|
|
24
|
-
const draft = cloneSpace(space);
|
|
25
|
-
const outcome = applyOperations(draft, env, ops);
|
|
26
|
-
if (outcome.errors.length > 0) return {
|
|
27
|
-
valid: false,
|
|
28
|
-
errors: outcome.errors,
|
|
29
|
-
warnings: validation.warnings
|
|
30
|
-
};
|
|
31
|
-
const audit = auditResources(draft, ops);
|
|
32
|
-
return {
|
|
33
|
-
valid: audit.errors.length === 0,
|
|
34
|
-
errors: audit.errors,
|
|
35
|
-
warnings: [...validation.warnings, ...audit.warnings]
|
|
17
|
+
errors: result.errors,
|
|
18
|
+
warnings: result.warnings
|
|
36
19
|
};
|
|
37
20
|
};
|
|
38
21
|
var validateTool = defineTool({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CssProps } from '@plitzi/sdk-authoring';
|
|
2
|
-
import { ElementRuntime, Environment } from '@plitzi/sdk-shared';
|
|
2
|
+
import { ElementRuntime, Environment, WhileRunning } from '@plitzi/sdk-shared';
|
|
3
3
|
export type Env = Environment;
|
|
4
4
|
/** CSS declarations for one selector state. Keys are kebab-case CSS properties (see plitzi://css-properties).
|
|
5
5
|
* Owned by the style engine, which is also what expands the shorthands an agent is allowed to write. */
|
|
@@ -138,6 +138,8 @@ export interface AIInteractionNode {
|
|
|
138
138
|
params?: Record<string, unknown>;
|
|
139
139
|
enabled?: boolean;
|
|
140
140
|
when?: unknown;
|
|
141
|
+
/** On a trigger: what firing it again does while its flow still runs. `skip` when absent. */
|
|
142
|
+
whileRunning?: WhileRunning;
|
|
141
143
|
/** Source element the callback targets (globalCallback/utility). Defaults to this element on write. */
|
|
142
144
|
elementId?: string;
|
|
143
145
|
preview?: Record<string, unknown>;
|
|
@@ -234,8 +236,13 @@ export interface AISchemaVariable {
|
|
|
234
236
|
* configuration. Every field is optional — a patch changes only the keys it sends. */
|
|
235
237
|
export interface AISettings {
|
|
236
238
|
customCss?: string;
|
|
239
|
+
/** Keep `runtime.state` — what `setState` writes — across reloads, filed under whoever is signed in. */
|
|
237
240
|
keepState?: boolean;
|
|
238
241
|
stateStorage?: 'localStorage' | 'sessionStorage';
|
|
242
|
+
/** Top-level state keys never kept, even with `keepState` on. */
|
|
243
|
+
transientState?: string[];
|
|
244
|
+
/** Kept keys the first paint shows, kept in a cookie too so the server renders with them. Small values only. */
|
|
245
|
+
paintedState?: string[];
|
|
239
246
|
/** `basic` covers any HTTP+JSON backend by configuration; anything else is a name someone registered. */
|
|
240
247
|
userProvider?: 'basic' | 'custom' | '' | (string & {});
|
|
241
248
|
tokenStorage?: 'localStorage' | 'sessionStorage' | '';
|
|
@@ -3,6 +3,12 @@ import { Operation } from '../tools/operations';
|
|
|
3
3
|
export type PreviewRequestBody = {
|
|
4
4
|
spaceId: number;
|
|
5
5
|
env?: string;
|
|
6
|
+
/**
|
|
7
|
+
* A published revision of `env` to render instead of its latest. Ignored for `main`, which has no revisions — it is
|
|
8
|
+
* the draft. Asked for when what is shown has to be one exact version: a reviewed one, rather than whatever was
|
|
9
|
+
* published after the review.
|
|
10
|
+
*/
|
|
11
|
+
revision?: number;
|
|
6
12
|
pageRef?: string;
|
|
7
13
|
operations?: Operation[];
|
|
8
14
|
/**
|
|
@@ -9,9 +9,20 @@ export type ScreenshotImage = {
|
|
|
9
9
|
mimeType: string;
|
|
10
10
|
data: string;
|
|
11
11
|
};
|
|
12
|
+
/**
|
|
13
|
+
* What a capture brings back of a page: how it looks, and how assistive technology reads it — the accessibility tree a
|
|
14
|
+
* screen reader walks, and the one a browser agent such as Claude in Chrome finds the page's controls in.
|
|
15
|
+
*/
|
|
16
|
+
export type CaptureView = 'image' | 'accessibility';
|
|
17
|
+
/** One viewport's accessibility tree, as an indented outline: `- button "Close"`, `- heading "Plans" [level=1]`. */
|
|
18
|
+
export type AccessibilityOutline = {
|
|
19
|
+
label: string;
|
|
20
|
+
outline: string;
|
|
21
|
+
};
|
|
12
22
|
export type ScreenshotResult = {
|
|
13
23
|
ok: true;
|
|
14
24
|
images: ScreenshotImage[];
|
|
25
|
+
accessibility?: AccessibilityOutline[];
|
|
15
26
|
} | {
|
|
16
27
|
ok: false;
|
|
17
28
|
error: string;
|
|
@@ -27,6 +38,11 @@ export type ScreenshotInput = {
|
|
|
27
38
|
* forces a theme keeps its own; left out, the browser's default applies, which is light.
|
|
28
39
|
*/
|
|
29
40
|
colorScheme?: ColorScheme;
|
|
41
|
+
/**
|
|
42
|
+
* What to bring back; `['image']` when left out. A browser that cannot read the accessibility tree answers without
|
|
43
|
+
* `accessibility`, and the caller says so rather than failing.
|
|
44
|
+
*/
|
|
45
|
+
views?: CaptureView[];
|
|
30
46
|
};
|
|
31
47
|
/** How plitzi_screenshot reaches the headless-browser service. The consumer injects an implementation (an HTTP
|
|
32
48
|
* client to the dedicated browser pod); absent → the tool is not registered. A capture that fails (pod down)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AIDefinition, AIElementDetail, ValidationError } from './aiSchema';
|
|
2
2
|
import { Operation } from '../tools/operations';
|
|
3
|
-
import { ActionEntry, ConnectorEntry, Schema, Style } from '@plitzi/sdk-shared';
|
|
3
|
+
import { ActionEntry, ConnectorEntry, FunctionsSaveResult, Schema, Style } from '@plitzi/sdk-shared';
|
|
4
4
|
/** The element schema, the style schema and the connectors persist independently (Space model / Style model / one
|
|
5
5
|
* row per connector), so each has its own optional persister. When one is absent, that store is applied in memory
|
|
6
6
|
* but reported unsaved. Connectors save and delete a row at a time — a batch touching two of them saves those two
|
|
@@ -12,6 +12,8 @@ export interface Persisters {
|
|
|
12
12
|
deleteConnector?: (connectorId: string) => Promise<void>;
|
|
13
13
|
saveAction?: (entry: ActionEntry) => Promise<void>;
|
|
14
14
|
deleteAction?: (actionId: string) => Promise<void>;
|
|
15
|
+
/** Saves the functions' files whole, against the version the batch read; answers what is wrong where it is. */
|
|
16
|
+
saveFunctions?: (files: Record<string, string>, base: string) => Promise<FunctionsSaveResult>;
|
|
15
17
|
}
|
|
16
18
|
export interface Conflict {
|
|
17
19
|
resourceUri: string;
|
|
@@ -66,6 +68,8 @@ export interface MutationOutcome {
|
|
|
66
68
|
/** The same, for actions: one row each, so a batch writes exactly the ones it touched. */
|
|
67
69
|
changedActions: string[];
|
|
68
70
|
deletedActions: string[];
|
|
71
|
+
/** The functions' files changed: they are saved whole, as one store. */
|
|
72
|
+
changedFunctions: boolean;
|
|
69
73
|
}
|
|
70
74
|
export interface ApplyInput {
|
|
71
75
|
environment?: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { computeVersion } from "../modules/mcp/helpers/computeVersion.js";
|
|
2
2
|
import { cloneSpace, findPageByRef, getPageElements } from "../modules/mcp/helpers/space.js";
|
|
3
|
-
import { applyOperations } from "../modules/mcp/tools/apply/dispatch.js";
|
|
4
3
|
import { validateOperations } from "../modules/mcp/tools/shared/validator/index.js";
|
|
4
|
+
import { applyOperations } from "../modules/mcp/tools/apply/dispatch.js";
|
|
5
5
|
import { randomUUID } from "node:crypto";
|
|
6
6
|
import { buildBody } from "@plitzi/sdk-server/ssr";
|
|
7
7
|
//#region src/preview/createPreview.ts
|
|
@@ -32,12 +32,12 @@ var syntheticRequest = (pagePath) => ({
|
|
|
32
32
|
* building any: this runs as a stage INSIDE an SSR server, not in the MCP process that calls it over HTTP. */
|
|
33
33
|
var createPreview = async (body, config, renderFn, pluginManager, caches) => {
|
|
34
34
|
const env = body.env ?? "main";
|
|
35
|
-
const revision = 0;
|
|
35
|
+
const revision = env === "main" ? 0 : body.revision ?? 0;
|
|
36
36
|
const offlineData = await config.adapters.getOfflineData(body.spaceId, env, revision);
|
|
37
37
|
if (!offlineData) return {
|
|
38
38
|
ok: false,
|
|
39
39
|
error: "NO_DATA",
|
|
40
|
-
message: `No offline data for space ${body.spaceId} (${env}).`
|
|
40
|
+
message: `No offline data for space ${body.spaceId} (${env}${revision ? ` revision ${revision}` : ""}).`
|
|
41
41
|
};
|
|
42
42
|
let draftOffline = offlineData;
|
|
43
43
|
if (body.operations && body.operations.length > 0) {
|
|
@@ -77,7 +77,8 @@ var createPreview = async (body, config, renderFn, pluginManager, caches) => {
|
|
|
77
77
|
token = randomUUID();
|
|
78
78
|
await config.draftStore.put(token, draftOffline, {
|
|
79
79
|
ttlMs,
|
|
80
|
-
reusable
|
|
80
|
+
reusable,
|
|
81
|
+
spaceId: body.spaceId
|
|
81
82
|
});
|
|
82
83
|
}
|
|
83
84
|
return {
|
package/dist/stages/preview.js
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { createPreview } from "../preview/createPreview.js";
|
|
2
2
|
import { readRawBody } from "@plitzi/sdk-server/kernel";
|
|
3
|
+
import { createHash, timingSafeEqual } from "node:crypto";
|
|
3
4
|
//#region src/stages/preview.ts
|
|
4
5
|
var PREVIEW_PATH_DEFAULT = "/__preview";
|
|
6
|
+
var digest = (value) => createHash("sha256").update(value).digest();
|
|
7
|
+
/**
|
|
8
|
+
* Whether the request carries the configured secret — and there has to BE one.
|
|
9
|
+
*
|
|
10
|
+
* This endpoint lives on the page server, which is what the public reaches on every published host. Treating a
|
|
11
|
+
* missing secret as "no check" turned an internal endpoint into a public one that renders any space's draft, so an
|
|
12
|
+
* enabled endpoint with no secret refuses everybody. Compared as digests, in constant time.
|
|
13
|
+
*/
|
|
14
|
+
var presentsSecret = (header, secret) => !!secret && typeof header === "string" && timingSafeEqual(digest(header), digest(secret));
|
|
5
15
|
var json = (ctx, status, payload) => {
|
|
6
16
|
ctx.res.setHeader("Content-Type", "application/json");
|
|
7
17
|
ctx.res.setStatus(status);
|
|
@@ -13,7 +23,7 @@ var previewStage = async (ctx) => {
|
|
|
13
23
|
const path = preview?.path ?? PREVIEW_PATH_DEFAULT;
|
|
14
24
|
const ending = ctx.req.path === `${path}/end`;
|
|
15
25
|
if (!preview?.enabled || ctx.req.method !== "POST" || ctx.req.path !== path && !ending) return false;
|
|
16
|
-
if (
|
|
26
|
+
if (!presentsSecret(ctx.req.headers["x-preview-secret"], preview.secret)) return json(ctx, 403, {
|
|
17
27
|
error: "FORBIDDEN",
|
|
18
28
|
message: "Invalid or missing preview secret."
|
|
19
29
|
});
|
|
@@ -49,6 +59,10 @@ var previewStage = async (ctx) => {
|
|
|
49
59
|
error: "BAD_REQUEST",
|
|
50
60
|
message: "spaceId (number) is required."
|
|
51
61
|
});
|
|
62
|
+
if (body.revision !== void 0 && (!Number.isInteger(body.revision) || body.revision < 1)) return json(ctx, 400, {
|
|
63
|
+
error: "BAD_REQUEST",
|
|
64
|
+
message: "revision must be a positive integer when given."
|
|
65
|
+
});
|
|
52
66
|
const result = await createPreview(body, ctx.config, ctx.renderFn, ctx.pluginManager, ctx.caches);
|
|
53
67
|
return json(ctx, result.ok ? 200 : 422, result);
|
|
54
68
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plitzi/sdk-mcp",
|
|
3
|
-
"version": "0.37.
|
|
3
|
+
"version": "0.37.3",
|
|
4
4
|
"license": "AGPL-3.0",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -29,14 +29,15 @@
|
|
|
29
29
|
"sideEffects": false,
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@modelcontextprotocol/ext-apps": "^1.7.5",
|
|
32
|
-
"@modelcontextprotocol/sdk": "^1.30.
|
|
33
|
-
"@plitzi/plitzi-sdk": "0.37.
|
|
34
|
-
"@plitzi/sdk-
|
|
35
|
-
"@plitzi/sdk-
|
|
36
|
-
"@plitzi/sdk-
|
|
37
|
-
"@plitzi/sdk-
|
|
38
|
-
"@plitzi/sdk-
|
|
39
|
-
"@plitzi/sdk-
|
|
32
|
+
"@modelcontextprotocol/sdk": "^1.30.1",
|
|
33
|
+
"@plitzi/plitzi-sdk": "0.37.3",
|
|
34
|
+
"@plitzi/sdk-authoring": "0.37.3",
|
|
35
|
+
"@plitzi/sdk-elements": "0.37.3",
|
|
36
|
+
"@plitzi/sdk-interactions": "0.37.3",
|
|
37
|
+
"@plitzi/sdk-schema": "0.37.3",
|
|
38
|
+
"@plitzi/sdk-server": "0.37.3",
|
|
39
|
+
"@plitzi/sdk-shared": "0.37.3",
|
|
40
|
+
"@plitzi/sdk-style": "0.37.3",
|
|
40
41
|
"ejs": "^6.0.1",
|
|
41
42
|
"esbuild": "^0.28.2",
|
|
42
43
|
"zod": "^4.6.5"
|
|
@@ -59,7 +60,7 @@
|
|
|
59
60
|
"react-dom": "^19.3.0",
|
|
60
61
|
"tsx": "^4.23.15",
|
|
61
62
|
"typescript": "^6.0.3",
|
|
62
|
-
"vite": "^8.3.
|
|
63
|
+
"vite": "^8.3.1",
|
|
63
64
|
"vite-plugin-dts": "^5.1.1",
|
|
64
65
|
"vitest": "^5.0.1"
|
|
65
66
|
},
|
|
@@ -52,12 +52,11 @@ Everything the widget needs travels in ONE call, as `operations`. Three ops carr
|
|
|
52
52
|
"flex-direction": "column",
|
|
53
53
|
"gap": "12px",
|
|
54
54
|
"padding": "16px",
|
|
55
|
-
"min-width": "0",
|
|
56
55
|
"color": "var(--color-text-primary, light-dark(#0f172a, #e8eaed))"
|
|
57
56
|
}
|
|
58
57
|
},
|
|
59
58
|
"row": {
|
|
60
|
-
"desktop": { "display": "flex", "flex-wrap": "wrap", "gap": "12px"
|
|
59
|
+
"desktop": { "display": "flex", "flex-wrap": "wrap", "gap": "12px" },
|
|
61
60
|
"mobile": { "flex-direction": "column" }
|
|
62
61
|
},
|
|
63
62
|
"tile": {
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { findElementByRef } from "../../../helpers/space.js";
|
|
2
|
-
import { definitionToAI, globalStyleToAI, idStyleToAI } from "../../operations/style/translator.js";
|
|
3
|
-
import { checkBindingSourceScope, checkBindingTarget, checkBindingTransformers } from "./bindings.js";
|
|
4
|
-
import { checkSlotCss } from "./css.js";
|
|
5
|
-
import { checkVariantApplication } from "./elements.js";
|
|
6
|
-
import { checkInteractionNode } from "./interactions.js";
|
|
7
|
-
import { buildValidationCtx } from "./index.js";
|
|
8
|
-
import { styleWithoutTag } from "@plitzi/sdk-schema/helpers/styleWithoutTag";
|
|
9
|
-
//#region src/modules/mcp/tools/shared/validator/audit.ts
|
|
10
|
-
var harvest = (ctx, into, label, run) => {
|
|
11
|
-
const sub = {
|
|
12
|
-
...ctx,
|
|
13
|
-
errors: [],
|
|
14
|
-
warnings: [],
|
|
15
|
-
warned: /* @__PURE__ */ new Set()
|
|
16
|
-
};
|
|
17
|
-
run(sub);
|
|
18
|
-
for (const e of sub.errors) into.errors.push({
|
|
19
|
-
path: e.path,
|
|
20
|
-
message: `Pre-existing malformation in ${label}: ${e.message}`,
|
|
21
|
-
hint: `${e.hint ? `${e.hint} ` : ""}This issue already exists in the space (NOT caused by your change), but the save is blocked until you fix it too, in this same batch.`,
|
|
22
|
-
...e.validValues ? { validValues: e.validValues } : {}
|
|
23
|
-
});
|
|
24
|
-
for (const w of sub.warnings) {
|
|
25
|
-
const message = `Pre-existing issue in ${label} (not caused by your change, but worth fixing too): ${w}`;
|
|
26
|
-
if (!into.warnings.includes(message)) into.warnings.push(message);
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
var toNode = (id, node) => ({
|
|
30
|
-
id,
|
|
31
|
-
title: node.title,
|
|
32
|
-
nodeType: node.type,
|
|
33
|
-
action: node.action,
|
|
34
|
-
params: node.params,
|
|
35
|
-
enabled: node.enabled,
|
|
36
|
-
when: node.when,
|
|
37
|
-
elementId: node.elementId ?? void 0,
|
|
38
|
-
preview: node.preview
|
|
39
|
-
});
|
|
40
|
-
var auditElement = (space, sub, el) => {
|
|
41
|
-
const hostRef = el.id;
|
|
42
|
-
const base = `element "${hostRef}"`;
|
|
43
|
-
for (const [category, list] of Object.entries(el.definition.bindings ?? {})) for (const binding of list) {
|
|
44
|
-
const path = `${base}.bindings.${category}[to=${binding.to}]`;
|
|
45
|
-
checkBindingTransformers(binding.transformers, `${path}.transformers`, sub);
|
|
46
|
-
checkBindingSourceScope(space, sub, hostRef, binding.source, `${path}.source`);
|
|
47
|
-
checkBindingTarget(hostRef, category, binding.to, path, sub);
|
|
48
|
-
}
|
|
49
|
-
for (const [id, node] of Object.entries(el.definition.interactions ?? {})) checkInteractionNode(toNode(id, node), `${base}.interactions.${id}`, sub, hostRef);
|
|
50
|
-
checkVariantApplication(el.definition.initialState, `${base}.initialState`, sub);
|
|
51
|
-
};
|
|
52
|
-
var auditDefinitionCss = (sub, label, def) => {
|
|
53
|
-
const { ref, slots, ...slot } = def;
|
|
54
|
-
checkSlotCss(slot, label, sub);
|
|
55
|
-
for (const [name, slotDef] of Object.entries(slots ?? {})) checkSlotCss(slotDef, `${label}.slots.${name}`, sub);
|
|
56
|
-
};
|
|
57
|
-
var collectTouched = (ops) => {
|
|
58
|
-
const touched = {
|
|
59
|
-
elementRefs: /* @__PURE__ */ new Set(),
|
|
60
|
-
definitions: /* @__PURE__ */ new Set(),
|
|
61
|
-
globalStyles: /* @__PURE__ */ new Set(),
|
|
62
|
-
idStyles: /* @__PURE__ */ new Set()
|
|
63
|
-
};
|
|
64
|
-
for (const op of ops) switch (op.type) {
|
|
65
|
-
case "upsertElement":
|
|
66
|
-
touched.elementRefs.add(op.element.ref);
|
|
67
|
-
break;
|
|
68
|
-
case "patchElement":
|
|
69
|
-
case "moveElement":
|
|
70
|
-
case "upsertBinding":
|
|
71
|
-
case "patchBinding":
|
|
72
|
-
case "deleteBinding":
|
|
73
|
-
case "upsertInteractionFlow":
|
|
74
|
-
case "patchInteractionNode":
|
|
75
|
-
case "deleteInteraction":
|
|
76
|
-
touched.elementRefs.add(op.ref);
|
|
77
|
-
break;
|
|
78
|
-
case "upsertDefinition":
|
|
79
|
-
case "patchDefinition":
|
|
80
|
-
touched.definitions.add(op.ref);
|
|
81
|
-
break;
|
|
82
|
-
case "upsertDefinitions":
|
|
83
|
-
for (const ref of Object.keys(op.definitions)) touched.definitions.add(ref);
|
|
84
|
-
break;
|
|
85
|
-
case "upsertGlobalStyle":
|
|
86
|
-
case "patchGlobalStyle":
|
|
87
|
-
touched.globalStyles.add(op.componentType);
|
|
88
|
-
break;
|
|
89
|
-
case "upsertIdStyle":
|
|
90
|
-
case "patchIdStyle": touched.idStyles.add(op.targetId);
|
|
91
|
-
}
|
|
92
|
-
return touched;
|
|
93
|
-
};
|
|
94
|
-
var auditResources = (space, ops) => {
|
|
95
|
-
const ctx = buildValidationCtx(space, ops);
|
|
96
|
-
const result = {
|
|
97
|
-
valid: true,
|
|
98
|
-
errors: [],
|
|
99
|
-
warnings: []
|
|
100
|
-
};
|
|
101
|
-
const touched = collectTouched(ops);
|
|
102
|
-
const seenElements = /* @__PURE__ */ new Set();
|
|
103
|
-
for (const ref of touched.elementRefs) {
|
|
104
|
-
const el = findElementByRef(space.schema, ref);
|
|
105
|
-
if (!el || seenElements.has(el.id)) continue;
|
|
106
|
-
seenElements.add(el.id);
|
|
107
|
-
harvest(ctx, result, `element "${el.id}"`, (sub) => auditElement(space, sub, el));
|
|
108
|
-
const tagless = styleWithoutTag(el, space.style);
|
|
109
|
-
if (tagless) result.warnings.push(`element "${el.id}" ${tagless}`);
|
|
110
|
-
}
|
|
111
|
-
for (const ref of touched.definitions) {
|
|
112
|
-
const def = definitionToAI(space.style, ref);
|
|
113
|
-
if (def) harvest(ctx, result, `definition "${ref}"`, (sub) => auditDefinitionCss(sub, `definition "${ref}"`, def));
|
|
114
|
-
}
|
|
115
|
-
for (const componentType of touched.globalStyles) {
|
|
116
|
-
const def = globalStyleToAI(space.style, componentType);
|
|
117
|
-
if (def) harvest(ctx, result, `global style "${componentType}"`, (sub) => auditDefinitionCss(sub, `global style "${componentType}"`, def));
|
|
118
|
-
}
|
|
119
|
-
for (const targetId of touched.idStyles) {
|
|
120
|
-
const def = idStyleToAI(space.style, targetId);
|
|
121
|
-
if (def) harvest(ctx, result, `id style "#${targetId}"`, (sub) => auditDefinitionCss(sub, `id style "#${targetId}"`, def));
|
|
122
|
-
}
|
|
123
|
-
result.valid = result.errors.length === 0;
|
|
124
|
-
return result;
|
|
125
|
-
};
|
|
126
|
-
//#endregion
|
|
127
|
-
export { auditResources };
|