@plitzi/sdk-server 0.32.14 → 0.32.16
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 +20 -0
- package/dist/adapters/jsonAdapters.js +2 -1
- package/dist/core/createServer.js +10 -2
- package/dist/core/http/stages/middlewares.js +3 -2
- package/dist/core/http/stages/pluginAssets.js +3 -2
- package/dist/core/http/stages/static.js +3 -2
- package/dist/core/requestParser.js +1 -1
- package/dist/core/server/{ssrServer.js → pageServer.js} +15 -6
- package/dist/core/services/registry.js +2 -2
- package/dist/helpers/cache/defaults.js +3 -3
- package/dist/index.js +1 -1
- package/dist/modules/mcp/catalogs/cssCatalog/border.js +30 -0
- package/dist/modules/mcp/catalogs/cssCatalog/box.js +31 -0
- package/dist/modules/mcp/catalogs/cssCatalog/helpers.js +185 -0
- package/dist/modules/mcp/catalogs/cssCatalog/index.js +131 -0
- package/dist/modules/mcp/catalogs/cssCatalog/layout.js +98 -0
- package/dist/modules/mcp/catalogs/cssCatalog/visual.js +144 -0
- package/dist/modules/mcp/catalogs/registry.js +1 -1
- package/dist/modules/mcp/helpers/guide.js +23 -13
- package/dist/modules/mcp/resources/core.js +5 -2
- package/dist/modules/mcp/resources/primer.js +2 -1
- package/dist/modules/mcp/resources/register.js +6 -3
- package/dist/modules/mcp/server.js +1 -1
- package/dist/modules/mcp/tools/operations/schema/interactions/upsertInteractionFlow.js +3 -2
- package/dist/modules/mcp/tools/operations/style/shared.js +2 -2
- package/dist/modules/mcp/tools/operations/style/write.js +2 -2
- package/dist/modules/mcp/tools/shared/validator/audit.js +1 -4
- package/dist/modules/mcp/tools/shared/validator/css.js +3 -11
- package/dist/modules/mcp/tools/shared/validator/elements.js +4 -1
- package/dist/modules/mcp/tools/shared/validator/index.js +15 -15
- package/dist/modules/mcp/tools/shared/validator/interactions.js +1 -4
- package/dist/modules/oauth/authorize.js +2 -1
- package/dist/modules/oauth/metadata.js +8 -2
- package/dist/modules/oauth/records.js +11 -2
- package/dist/modules/oauth/register.js +15 -8
- package/dist/modules/oauth/respond.js +4 -4
- package/dist/modules/oauth/token.js +2 -2
- package/dist/modules/ssr/registerExternalPlugins.js +1 -1
- package/dist/plugins/manager.js +1 -1
- package/dist/src/core/createServer.d.ts +9 -1
- package/dist/src/core/server/pageServer.d.ts +10 -0
- package/dist/src/core/services/registry.d.ts +1 -1
- package/dist/src/modules/mcp/catalogs/cssCatalog/border.d.ts +4 -0
- package/dist/src/modules/mcp/catalogs/cssCatalog/box.d.ts +4 -0
- package/dist/src/modules/mcp/catalogs/cssCatalog/helpers.d.ts +34 -0
- package/dist/src/modules/mcp/catalogs/{cssCatalog.d.ts → cssCatalog/index.d.ts} +10 -5
- package/dist/src/modules/mcp/catalogs/cssCatalog/layout.d.ts +9 -0
- package/dist/src/modules/mcp/catalogs/cssCatalog/visual.d.ts +8 -0
- package/dist/src/modules/mcp/helpers/guide.d.ts +2 -2
- package/dist/src/modules/mcp/resources/index.d.ts +1 -1
- package/dist/src/modules/mcp/tests/apply.test.d.ts +1 -0
- package/dist/src/modules/mcp/tests/bindings.test.d.ts +1 -0
- package/dist/src/modules/mcp/tests/css.test.d.ts +1 -0
- package/dist/src/modules/mcp/tests/helpers.d.ts +13 -0
- package/dist/src/modules/mcp/tests/interactions.test.d.ts +1 -0
- package/dist/src/modules/mcp/tests/pages.test.d.ts +1 -0
- package/dist/src/modules/mcp/tests/reads.test.d.ts +1 -0
- package/dist/src/modules/mcp/tests/search.test.d.ts +1 -0
- package/dist/src/modules/mcp/tests/styles.test.d.ts +1 -0
- package/dist/src/modules/mcp/tests/tools.test.d.ts +1 -0
- package/dist/src/modules/mcp/tests/validation.test.d.ts +1 -0
- package/dist/src/modules/mcp/tools/screenshot.d.ts +1 -1
- package/dist/src/modules/oauth/metadata.d.ts +7 -1
- package/dist/src/modules/oauth/records.d.ts +6 -0
- package/package.json +9 -9
- package/dist/modules/mcp/catalogs/cssCatalog.js +0 -205
- package/dist/src/core/server/ssrServer.d.ts +0 -2
- /package/dist/src/modules/mcp/{mcp.test.d.ts → catalogs/cssCatalog/cssCatalog.test.d.ts} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @plitzi/sdk-server
|
|
2
2
|
|
|
3
|
+
## 0.32.16
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- v0.32.16
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @plitzi/plitzi-sdk@0.32.16
|
|
10
|
+
- @plitzi/sdk-schema@0.32.16
|
|
11
|
+
- @plitzi/sdk-shared@0.32.16
|
|
12
|
+
|
|
13
|
+
## 0.32.15
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- v0.32.15
|
|
18
|
+
- Updated dependencies
|
|
19
|
+
- @plitzi/plitzi-sdk@0.32.15
|
|
20
|
+
- @plitzi/sdk-schema@0.32.15
|
|
21
|
+
- @plitzi/sdk-shared@0.32.15
|
|
22
|
+
|
|
3
23
|
## 0.32.14
|
|
4
24
|
|
|
5
25
|
### Patch Changes
|
|
@@ -13,7 +13,8 @@ var createJsonAdapters = (config) => {
|
|
|
13
13
|
}
|
|
14
14
|
};
|
|
15
15
|
const saveOfflineData = (spaceId, environment, data) => {
|
|
16
|
-
|
|
16
|
+
const filePath = typeof config.offlineData === "function" ? config.offlineData(spaceId, environment) : config.offlineData;
|
|
17
|
+
writeFileSync(filePath, JSON.stringify(data, null, 2));
|
|
17
18
|
return Promise.resolve();
|
|
18
19
|
};
|
|
19
20
|
const getSpaceDeployment = (req) => {
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import { createMCPServer } from "./server/mcpServer.js";
|
|
2
2
|
import { resolveServices } from "./services/resolve.js";
|
|
3
|
-
import { createSSRServer } from "./server/
|
|
3
|
+
import { createPageServer, createSSRServer } from "./server/pageServer.js";
|
|
4
4
|
//#region src/core/createServer.ts
|
|
5
|
+
/** The general server: mounts whatever the config asks for — SSR, RSC and MCP together, if that is what it
|
|
6
|
+
* enables — and is what a deployment reaches for when it serves more than one surface from a single port (the
|
|
7
|
+
* package's own standalone harness does exactly that).
|
|
8
|
+
*
|
|
9
|
+
* Prefer the surface-specific factories whenever the surface IS known, so the process states what it serves by
|
|
10
|
+
* the name it calls: createSSRServer for pages + RSC, createMCPServer for a dedicated MCP server. An MCP-only
|
|
11
|
+
* config is handed to the latter here, since the lean pipeline builds none of the render template, caches or
|
|
12
|
+
* plugin manager a page server would carry unused. */
|
|
5
13
|
var createServer = (config) => {
|
|
6
14
|
const services = resolveServices(config);
|
|
7
15
|
if (services.mcp && !services.ssr && !services.rsc && !services.ai) return createMCPServer(config);
|
|
8
|
-
return
|
|
16
|
+
return createPageServer(config, services);
|
|
9
17
|
};
|
|
10
18
|
//#endregion
|
|
11
19
|
export { createMCPServer, createSSRServer, createServer, resolveServices };
|
|
@@ -5,12 +5,13 @@ import { spaceDeploymentMiddleware } from "../../../middlewares/spaceDeployment.
|
|
|
5
5
|
//#region src/core/http/stages/middlewares.ts
|
|
6
6
|
var middlewaresStage = async (ctx) => {
|
|
7
7
|
const { config, req, res } = ctx;
|
|
8
|
-
|
|
8
|
+
const middlewares = [
|
|
9
9
|
spaceDeploymentMiddleware(config.adapters),
|
|
10
10
|
basicAuthMiddleware({ cacheTtlMs: config.cacheTtlMs }),
|
|
11
11
|
authMiddleware(config.adapters),
|
|
12
12
|
...config.middlewares || []
|
|
13
|
-
]
|
|
13
|
+
];
|
|
14
|
+
return await runMiddlewares(middlewares, req, res) || res.status !== 200;
|
|
14
15
|
};
|
|
15
16
|
//#endregion
|
|
16
17
|
export { middlewaresStage };
|
|
@@ -7,10 +7,11 @@ var pluginAssetsStage = async (ctx) => {
|
|
|
7
7
|
const pluginName = relative.split("/")[1];
|
|
8
8
|
if (pluginName && pluginManager.hasPlugin(pluginName)) {
|
|
9
9
|
await pluginManager.prepare(pluginName);
|
|
10
|
-
|
|
10
|
+
const strippedReq = {
|
|
11
11
|
...req,
|
|
12
12
|
path: relative
|
|
13
|
-
}
|
|
13
|
+
};
|
|
14
|
+
if (serveStatic(strippedReq, res, pluginManager.outputDir)) return true;
|
|
14
15
|
}
|
|
15
16
|
res.setStatus(404);
|
|
16
17
|
res.send("Not found");
|
|
@@ -20,10 +20,11 @@ var configStaticStage = (ctx) => {
|
|
|
20
20
|
if (ctx.req.path === prefix || ctx.req.path.startsWith(normalizedPrefix)) {
|
|
21
21
|
ctx.res.setHeader("Access-Control-Allow-Origin", "*");
|
|
22
22
|
ctx.res.setHeader("Cross-Origin-Resource-Policy", "cross-origin");
|
|
23
|
-
|
|
23
|
+
const strippedReq = {
|
|
24
24
|
...ctx.req,
|
|
25
25
|
path: ctx.req.path.slice(prefix.length) || "/"
|
|
26
|
-
}
|
|
26
|
+
};
|
|
27
|
+
if (serveStatic(strippedReq, ctx.res, rootDir)) return true;
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
30
|
return false;
|
|
@@ -47,7 +47,7 @@ var clientIp = (raw, req) => {
|
|
|
47
47
|
const forwardedFor = headerValue(req.headers["x-forwarded-for"]).split(",")[0] ?? "";
|
|
48
48
|
return unmapIpv4(headerValue(req.headers["cf-connecting-ip"]) || headerValue(req.headers["x-real-ip"]) || forwardedFor.trim() || raw.socket.remoteAddress || "");
|
|
49
49
|
};
|
|
50
|
-
var MAX_BODY_BYTES =
|
|
50
|
+
var MAX_BODY_BYTES = 1048576;
|
|
51
51
|
var readRawBody = (raw) => new Promise((resolve, reject) => {
|
|
52
52
|
const chunks = [];
|
|
53
53
|
let size = 0;
|
|
@@ -4,16 +4,18 @@ import { DEFAULT_TTL_MS } from "../../helpers/cache/defaults.js";
|
|
|
4
4
|
import { buildCacheManager } from "../../helpers/cache/cacheManager.js";
|
|
5
5
|
import { createServerCaches, destroyServerCaches } from "../../helpers/cache/serverCaches.js";
|
|
6
6
|
import { createMemoryDraftStore } from "../../modules/ssr/preview.js";
|
|
7
|
-
import {
|
|
7
|
+
import { buildPagePipeline } from "../services/registry.js";
|
|
8
8
|
import normalizePlugins, { normalizePluginSource } from "../../helpers/normalizePlugins.js";
|
|
9
9
|
import { compileTemplate } from "../../modules/ssr/template.js";
|
|
10
10
|
import { PluginManager } from "../../plugins/manager.js";
|
|
11
11
|
import { resolveServices } from "../services/resolve.js";
|
|
12
|
-
//#region src/core/server/
|
|
13
|
-
|
|
12
|
+
//#region src/core/server/pageServer.ts
|
|
13
|
+
/** The page-serving machinery: html/rsc caches, the render template and the plugin manager, driving the page
|
|
14
|
+
* pipeline. Which services it mounts is the CALLER's decision — {@link createServer} passes whatever the config
|
|
15
|
+
* enables, createSSRServer pins the page surface — so this unit never second-guesses a factory's promise. */
|
|
16
|
+
var createPageServer = (config, services) => {
|
|
14
17
|
const { cacheTtlMs: htmlTtlMs = DEFAULT_TTL_MS.html } = config;
|
|
15
18
|
if (config.preview?.enabled && !config.draftStore) config.draftStore = createMemoryDraftStore();
|
|
16
|
-
const services = resolveServices(config);
|
|
17
19
|
const caches = createServerCaches(htmlTtlMs, config.rsc?.cacheTtlMs ?? DEFAULT_TTL_MS.rsc);
|
|
18
20
|
const cache = caches.html ? buildCacheManager(caches.html) : null;
|
|
19
21
|
const renderFn = config.templateFn ?? compileTemplate();
|
|
@@ -25,7 +27,7 @@ var createSSRServer = (config) => {
|
|
|
25
27
|
},
|
|
26
28
|
invalidate: (name, version) => pluginManager.invalidate(name, version)
|
|
27
29
|
};
|
|
28
|
-
const stages =
|
|
30
|
+
const stages = buildPagePipeline(services);
|
|
29
31
|
const makeHandlerForPort = (port) => {
|
|
30
32
|
const buildContext = (raw, rawRes, req, res) => ({
|
|
31
33
|
raw,
|
|
@@ -50,5 +52,12 @@ var createSSRServer = (config) => {
|
|
|
50
52
|
}
|
|
51
53
|
});
|
|
52
54
|
};
|
|
55
|
+
/** The page server: SSR and RSC, and nothing else. MCP is pinned OFF here even when the config asks for it —
|
|
56
|
+
* what a factory named after a surface mounts must be readable from its name alone. A deployment that wants
|
|
57
|
+
* both surfaces in one process asks {@link createServer} for them; a dedicated MCP server is createMCPServer. */
|
|
58
|
+
var createSSRServer = (config) => createPageServer(config, {
|
|
59
|
+
...resolveServices(config),
|
|
60
|
+
mcp: false
|
|
61
|
+
});
|
|
53
62
|
//#endregion
|
|
54
|
-
export { createSSRServer };
|
|
63
|
+
export { createPageServer, createSSRServer };
|
|
@@ -9,7 +9,7 @@ import { middlewaresStage } from "../http/stages/middlewares.js";
|
|
|
9
9
|
import { pluginAssetsStage } from "../http/stages/pluginAssets.js";
|
|
10
10
|
import { builtinPublicStage, configStaticStage, publicDirStage, wellKnownStage } from "../http/stages/static.js";
|
|
11
11
|
//#region src/core/services/registry.ts
|
|
12
|
-
var
|
|
12
|
+
var buildPagePipeline = (services) => {
|
|
13
13
|
const stages = [
|
|
14
14
|
healthStage,
|
|
15
15
|
builtinPublicStage,
|
|
@@ -34,4 +34,4 @@ var buildMCPPipeline = () => [
|
|
|
34
34
|
mcpOnlyStage
|
|
35
35
|
];
|
|
36
36
|
//#endregion
|
|
37
|
-
export { buildMCPPipeline,
|
|
37
|
+
export { buildMCPPipeline, buildPagePipeline };
|
package/dist/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import { createMemoryDraftStore, createPreview } from "./modules/ssr/preview.js"
|
|
|
16
16
|
import { buildHealthPayload, registerHealthCheck } from "./core/health.js";
|
|
17
17
|
import { createMCPServer } from "./core/server/mcpServer.js";
|
|
18
18
|
import { resolveServices } from "./core/services/resolve.js";
|
|
19
|
-
import { createSSRServer } from "./core/server/
|
|
19
|
+
import { createSSRServer } from "./core/server/pageServer.js";
|
|
20
20
|
import { createServer } from "./core/createServer.js";
|
|
21
21
|
import { createJsonAdapters } from "./adapters/jsonAdapters.js";
|
|
22
22
|
import AIEngine from "./modules/ai/AIEngine.js";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { BORDER_STYLES, BORDER_WIDTH_KEYWORDS, LENGTH_RE, SIDES, expandTo4, splitTokens } from "./helpers.js";
|
|
2
|
+
//#region src/modules/mcp/catalogs/cssCatalog/border.ts
|
|
3
|
+
var BORDER_INITIAL = {
|
|
4
|
+
width: "medium",
|
|
5
|
+
style: "none",
|
|
6
|
+
color: "currentcolor"
|
|
7
|
+
};
|
|
8
|
+
var classifyBorderToken = (token) => {
|
|
9
|
+
if (BORDER_STYLES.has(token)) return "style";
|
|
10
|
+
if (BORDER_WIDTH_KEYWORDS.has(token) || LENGTH_RE.test(token)) return "width";
|
|
11
|
+
return "color";
|
|
12
|
+
};
|
|
13
|
+
var classifyBorderTokens = (value) => {
|
|
14
|
+
const parts = { ...BORDER_INITIAL };
|
|
15
|
+
for (const token of splitTokens(value)) parts[classifyBorderToken(token)] = token;
|
|
16
|
+
return parts;
|
|
17
|
+
};
|
|
18
|
+
var expandBorder = (key, value, out) => {
|
|
19
|
+
const sides = key === "border" ? SIDES : [key.slice(7)];
|
|
20
|
+
const parts = classifyBorderTokens(value);
|
|
21
|
+
for (const side of sides) for (const [kind, token] of Object.entries(parts)) out[`border-${side}-${kind}`] = token;
|
|
22
|
+
};
|
|
23
|
+
var expandBorderGroup = (suffix, value, out) => {
|
|
24
|
+
const values = expandTo4(splitTokens(value));
|
|
25
|
+
SIDES.forEach((side, index) => {
|
|
26
|
+
out[`border-${side}-${suffix}`] = values[index];
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
//#endregion
|
|
30
|
+
export { classifyBorderTokens, expandBorder, expandBorderGroup };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { expandTo4, splitPair, splitTokens } from "./helpers.js";
|
|
2
|
+
//#region src/modules/mcp/catalogs/cssCatalog/box.ts
|
|
3
|
+
var CORNERS = [
|
|
4
|
+
"top-left",
|
|
5
|
+
"top-right",
|
|
6
|
+
"bottom-right",
|
|
7
|
+
"bottom-left"
|
|
8
|
+
];
|
|
9
|
+
var expandBox = (key, value, out) => {
|
|
10
|
+
const [top, right, bottom, left] = expandTo4(splitTokens(value));
|
|
11
|
+
const prefix = key === "inset" ? "" : `${key}-`;
|
|
12
|
+
out[`${prefix}top`] = top;
|
|
13
|
+
out[`${prefix}right`] = right;
|
|
14
|
+
out[`${prefix}bottom`] = bottom;
|
|
15
|
+
out[`${prefix}left`] = left;
|
|
16
|
+
};
|
|
17
|
+
var expandBorderRadius = (value, out) => {
|
|
18
|
+
const [horizontal, vertical] = splitPair(value, "/");
|
|
19
|
+
const h = expandTo4(splitTokens(horizontal));
|
|
20
|
+
const v = vertical === void 0 ? void 0 : expandTo4(splitTokens(vertical));
|
|
21
|
+
CORNERS.forEach((corner, index) => {
|
|
22
|
+
out[`border-${corner}-radius`] = v ? `${h[index]} ${v[index]}` : h[index];
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
var expandGap = (value, out) => {
|
|
26
|
+
const [row, col = row] = splitTokens(value);
|
|
27
|
+
out["row-gap"] = row;
|
|
28
|
+
out["column-gap"] = col;
|
|
29
|
+
};
|
|
30
|
+
//#endregion
|
|
31
|
+
export { expandBorderRadius, expandBox, expandGap };
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
//#region src/modules/mcp/catalogs/cssCatalog/helpers.ts
|
|
2
|
+
var walkTopLevel = (value, onChar) => {
|
|
3
|
+
let depth = 0;
|
|
4
|
+
let quote = "";
|
|
5
|
+
for (const ch of value) {
|
|
6
|
+
if (quote) {
|
|
7
|
+
onChar(ch, false);
|
|
8
|
+
if (ch === quote) quote = "";
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
if (/["']/.test(ch)) {
|
|
12
|
+
quote = ch;
|
|
13
|
+
onChar(ch, false);
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
if (ch === "(") depth += 1;
|
|
17
|
+
else if (ch === ")") depth = Math.max(0, depth - 1);
|
|
18
|
+
onChar(ch, depth === 0);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
/** Split a shorthand value into whitespace-separated tokens, keeping parenthesized and quoted runs whole. */
|
|
22
|
+
var splitTokens = (value) => {
|
|
23
|
+
const tokens = [];
|
|
24
|
+
let current = "";
|
|
25
|
+
walkTopLevel(value.trim(), (ch, topLevel) => {
|
|
26
|
+
if (topLevel && /\s/.test(ch)) {
|
|
27
|
+
if (current) {
|
|
28
|
+
tokens.push(current);
|
|
29
|
+
current = "";
|
|
30
|
+
}
|
|
31
|
+
} else current += ch;
|
|
32
|
+
});
|
|
33
|
+
if (current) tokens.push(current);
|
|
34
|
+
return tokens;
|
|
35
|
+
};
|
|
36
|
+
/** Split a value on a top-level separator. Used for the comma-separated layers of transition/animation/background
|
|
37
|
+
* and for the `/` of border-radius/grid-template/grid-row. */
|
|
38
|
+
var splitOn = (value, separator) => {
|
|
39
|
+
const parts = [];
|
|
40
|
+
let current = "";
|
|
41
|
+
walkTopLevel(value, (ch, topLevel) => {
|
|
42
|
+
if (topLevel && ch === separator) {
|
|
43
|
+
parts.push(current);
|
|
44
|
+
current = "";
|
|
45
|
+
} else current += ch;
|
|
46
|
+
});
|
|
47
|
+
parts.push(current);
|
|
48
|
+
return parts.map((part) => part.trim());
|
|
49
|
+
};
|
|
50
|
+
/** Split a value on its first top-level `/` — the `a / b` form of border-radius, grid-template, background and
|
|
51
|
+
* font. Returns the half after the slash as undefined when there is none, which is what tells those expanders the
|
|
52
|
+
* second half was not declared at all. */
|
|
53
|
+
var splitPair = (value, separator) => {
|
|
54
|
+
const [first, ...rest] = splitOn(value, separator);
|
|
55
|
+
return [first, rest.length > 0 ? rest.join(separator) : void 0];
|
|
56
|
+
};
|
|
57
|
+
var TIME_RE = /^-?[\d.]+(ms|s)$/;
|
|
58
|
+
var NUMBER_RE = /^-?[\d.]+$/;
|
|
59
|
+
/** A numeric value with an optional unit or `%` — matches both `12px` and `50%`. */
|
|
60
|
+
var LENGTH_RE = /^-?[\d.]+([a-z%]*)$/;
|
|
61
|
+
var QUOTE_RE = /^['"]/;
|
|
62
|
+
/** Resolve the 1–4 value syntax shared by padding/margin/inset (top/right/bottom/left) and border-radius
|
|
63
|
+
* (top-left/top-right/bottom-right/bottom-left): 1 value fills all, 2 pair up, 3 leave the 4th mirroring the 2nd. */
|
|
64
|
+
var expandTo4 = (tokens) => {
|
|
65
|
+
const [a, b = a, c = a, d = b] = tokens;
|
|
66
|
+
return [
|
|
67
|
+
a,
|
|
68
|
+
b,
|
|
69
|
+
c,
|
|
70
|
+
d
|
|
71
|
+
];
|
|
72
|
+
};
|
|
73
|
+
var SIDES = [
|
|
74
|
+
"top",
|
|
75
|
+
"right",
|
|
76
|
+
"bottom",
|
|
77
|
+
"left"
|
|
78
|
+
];
|
|
79
|
+
var FLEX_DIRECTIONS = /* @__PURE__ */ new Set([
|
|
80
|
+
"row",
|
|
81
|
+
"row-reverse",
|
|
82
|
+
"column",
|
|
83
|
+
"column-reverse"
|
|
84
|
+
]);
|
|
85
|
+
var FLEX_WRAPS = /* @__PURE__ */ new Set([
|
|
86
|
+
"nowrap",
|
|
87
|
+
"wrap",
|
|
88
|
+
"wrap-reverse"
|
|
89
|
+
]);
|
|
90
|
+
var LIST_STYLE_TYPES = /* @__PURE__ */ new Set([
|
|
91
|
+
"disc",
|
|
92
|
+
"circle",
|
|
93
|
+
"square",
|
|
94
|
+
"decimal",
|
|
95
|
+
"decimal-leading-zero",
|
|
96
|
+
"lower-roman",
|
|
97
|
+
"upper-roman",
|
|
98
|
+
"lower-alpha",
|
|
99
|
+
"upper-alpha",
|
|
100
|
+
"lower-greek",
|
|
101
|
+
"lower-latin",
|
|
102
|
+
"upper-latin",
|
|
103
|
+
"armenian",
|
|
104
|
+
"georgian",
|
|
105
|
+
"none"
|
|
106
|
+
]);
|
|
107
|
+
var LIST_STYLE_POSITIONS = /* @__PURE__ */ new Set(["inside", "outside"]);
|
|
108
|
+
var TEXT_DECORATION_LINES = /* @__PURE__ */ new Set([
|
|
109
|
+
"underline",
|
|
110
|
+
"overline",
|
|
111
|
+
"line-through",
|
|
112
|
+
"blink",
|
|
113
|
+
"none"
|
|
114
|
+
]);
|
|
115
|
+
var TEXT_DECORATION_STYLES = /* @__PURE__ */ new Set([
|
|
116
|
+
"solid",
|
|
117
|
+
"double",
|
|
118
|
+
"dotted",
|
|
119
|
+
"dashed",
|
|
120
|
+
"wavy"
|
|
121
|
+
]);
|
|
122
|
+
var TIMING_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
123
|
+
"ease",
|
|
124
|
+
"linear",
|
|
125
|
+
"ease-in",
|
|
126
|
+
"ease-out",
|
|
127
|
+
"ease-in-out",
|
|
128
|
+
"step-start",
|
|
129
|
+
"step-end"
|
|
130
|
+
]);
|
|
131
|
+
/** A timing function may also be a function call — cubic-bezier(…) or steps(…). */
|
|
132
|
+
var isTimingFunction = (token) => TIMING_FUNCTIONS.has(token) || token.startsWith("cubic-bezier(") || token.startsWith("steps(");
|
|
133
|
+
var ANIMATION_DIRECTIONS = /* @__PURE__ */ new Set([
|
|
134
|
+
"normal",
|
|
135
|
+
"reverse",
|
|
136
|
+
"alternate",
|
|
137
|
+
"alternate-reverse"
|
|
138
|
+
]);
|
|
139
|
+
var ANIMATION_FILL_MODES = /* @__PURE__ */ new Set([
|
|
140
|
+
"none",
|
|
141
|
+
"forwards",
|
|
142
|
+
"backwards",
|
|
143
|
+
"both"
|
|
144
|
+
]);
|
|
145
|
+
var ANIMATION_PLAY_STATES = /* @__PURE__ */ new Set(["running", "paused"]);
|
|
146
|
+
var FONT_STYLE_KEYWORDS = /* @__PURE__ */ new Set([
|
|
147
|
+
"normal",
|
|
148
|
+
"italic",
|
|
149
|
+
"oblique"
|
|
150
|
+
]);
|
|
151
|
+
var FONT_VARIANT_KEYWORDS = /* @__PURE__ */ new Set(["normal", "small-caps"]);
|
|
152
|
+
var FONT_WEIGHT_KEYWORDS = /* @__PURE__ */ new Set([
|
|
153
|
+
"normal",
|
|
154
|
+
"bold",
|
|
155
|
+
"bolder",
|
|
156
|
+
"lighter",
|
|
157
|
+
"100",
|
|
158
|
+
"200",
|
|
159
|
+
"300",
|
|
160
|
+
"400",
|
|
161
|
+
"500",
|
|
162
|
+
"600",
|
|
163
|
+
"700",
|
|
164
|
+
"800",
|
|
165
|
+
"900"
|
|
166
|
+
]);
|
|
167
|
+
var BORDER_STYLES = /* @__PURE__ */ new Set([
|
|
168
|
+
"none",
|
|
169
|
+
"hidden",
|
|
170
|
+
"dotted",
|
|
171
|
+
"dashed",
|
|
172
|
+
"solid",
|
|
173
|
+
"double",
|
|
174
|
+
"groove",
|
|
175
|
+
"ridge",
|
|
176
|
+
"inset",
|
|
177
|
+
"outset"
|
|
178
|
+
]);
|
|
179
|
+
var BORDER_WIDTH_KEYWORDS = /* @__PURE__ */ new Set([
|
|
180
|
+
"thin",
|
|
181
|
+
"medium",
|
|
182
|
+
"thick"
|
|
183
|
+
]);
|
|
184
|
+
//#endregion
|
|
185
|
+
export { ANIMATION_DIRECTIONS, ANIMATION_FILL_MODES, ANIMATION_PLAY_STATES, BORDER_STYLES, BORDER_WIDTH_KEYWORDS, FLEX_DIRECTIONS, FLEX_WRAPS, FONT_STYLE_KEYWORDS, FONT_VARIANT_KEYWORDS, FONT_WEIGHT_KEYWORDS, LENGTH_RE, LIST_STYLE_POSITIONS, LIST_STYLE_TYPES, NUMBER_RE, QUOTE_RE, SIDES, TEXT_DECORATION_LINES, TEXT_DECORATION_STYLES, TIME_RE, expandTo4, isTimingFunction, splitOn, splitPair, splitTokens };
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { SIDES } from "./helpers.js";
|
|
2
|
+
import { expandBorder, expandBorderGroup } from "./border.js";
|
|
3
|
+
import { expandBorderRadius, expandBox, expandGap } from "./box.js";
|
|
4
|
+
import { expandColumns, expandFlex, expandFlexFlow, expandGridArea, expandGridLine, expandGridTemplate, expandOverflow, expandPlacePair } from "./layout.js";
|
|
5
|
+
import { expandAnimation, expandBackground, expandFont, expandListStyle, expandOutline, expandTextDecoration, expandTransition } from "./visual.js";
|
|
6
|
+
import styleConstants from "@plitzi/sdk-shared/style/styleConstants";
|
|
7
|
+
//#region src/modules/mcp/catalogs/cssCatalog/index.ts
|
|
8
|
+
/** Every kebab-case CSS property Plitzi's style engine understands. Values written to a definition
|
|
9
|
+
* must use these exact keys — camelCase or unknown keys are rejected by the validator. */
|
|
10
|
+
var cssProperties = Array.from(new Set(Object.values(styleConstants))).sort();
|
|
11
|
+
var cssPropertySet = new Set(cssProperties);
|
|
12
|
+
var isCssProperty = (key) => cssPropertySet.has(key);
|
|
13
|
+
var toKebab = (key) => key.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
14
|
+
/** If a camelCase key maps to a known kebab-case property, return it — used to teach the agent the correct key. */
|
|
15
|
+
var suggestCssProperty = (key) => {
|
|
16
|
+
const kebab = toKebab(key);
|
|
17
|
+
return cssPropertySet.has(kebab) ? kebab : void 0;
|
|
18
|
+
};
|
|
19
|
+
var EXPANDERS = /* @__PURE__ */ new Map([
|
|
20
|
+
["padding", expandBox],
|
|
21
|
+
["margin", expandBox],
|
|
22
|
+
["inset", expandBox],
|
|
23
|
+
["border-radius", (_key, value, out) => expandBorderRadius(value, out)],
|
|
24
|
+
["gap", (_key, value, out) => expandGap(value, out)],
|
|
25
|
+
["border-width", (key, value, out) => expandBorderGroup(key.slice(7), value, out)],
|
|
26
|
+
["border-color", (key, value, out) => expandBorderGroup(key.slice(7), value, out)],
|
|
27
|
+
["border-style", (key, value, out) => expandBorderGroup(key.slice(7), value, out)],
|
|
28
|
+
["border", expandBorder],
|
|
29
|
+
["overflow", (_key, value, out) => expandOverflow(value, out)],
|
|
30
|
+
["flex", (_key, value, out) => expandFlex(value, out)],
|
|
31
|
+
["flex-flow", (_key, value, out) => expandFlexFlow(value, out)],
|
|
32
|
+
["place-content", (_key, value, out) => expandPlacePair("place-content", value, out)],
|
|
33
|
+
["place-items", (_key, value, out) => expandPlacePair("place-items", value, out)],
|
|
34
|
+
["place-self", (_key, value, out) => expandPlacePair("place-self", value, out)],
|
|
35
|
+
["grid-row", (_key, value, out) => expandGridLine("grid-row", value, out)],
|
|
36
|
+
["grid-column", (_key, value, out) => expandGridLine("grid-column", value, out)],
|
|
37
|
+
["grid-area", (_key, value, out) => expandGridArea(value, out)],
|
|
38
|
+
["grid", (_key, value, out) => expandGridTemplate(value, out)],
|
|
39
|
+
["grid-template", (_key, value, out) => expandGridTemplate(value, out)],
|
|
40
|
+
["columns", (_key, value, out) => expandColumns(value, out)],
|
|
41
|
+
["outline", (_key, value, out) => expandOutline(value, out)],
|
|
42
|
+
["list-style", (_key, value, out) => expandListStyle(value, out)],
|
|
43
|
+
["text-decoration", (_key, value, out) => expandTextDecoration(value, out)],
|
|
44
|
+
["transition", (_key, value, out) => expandTransition(value, out)],
|
|
45
|
+
["animation", (_key, value, out) => expandAnimation(value, out)],
|
|
46
|
+
["background", (_key, value, out) => expandBackground(value, out)],
|
|
47
|
+
["font", (_key, value, out) => expandFont(value, out)]
|
|
48
|
+
]);
|
|
49
|
+
for (const side of SIDES) EXPANDERS.set(`border-${side}`, expandBorder);
|
|
50
|
+
/** Every shorthand the MCP accepts and atomizes — advertised to the agent so it knows it may write plain CSS. */
|
|
51
|
+
var cssShorthands = Array.from(EXPANDERS.keys()).sort();
|
|
52
|
+
var expandOne = (key, raw, out) => {
|
|
53
|
+
const expand = EXPANDERS.get(key);
|
|
54
|
+
if (!expand) return false;
|
|
55
|
+
const value = String(raw).trim();
|
|
56
|
+
if (value !== "") expand(key, value, out);
|
|
57
|
+
return true;
|
|
58
|
+
};
|
|
59
|
+
/** Expand supported CSS shorthands to their longhand keys. Explicit longhands in the same map win over any
|
|
60
|
+
* expansion (so `{ padding: 8, padding-left: 0 }` keeps padding-left: 0). Unrecognized keys pass through. */
|
|
61
|
+
var expandShorthand = (css) => {
|
|
62
|
+
const expanded = {};
|
|
63
|
+
const direct = {};
|
|
64
|
+
for (const [key, value] of Object.entries(css)) if (!expandOne(key, value, expanded)) direct[key] = value;
|
|
65
|
+
return {
|
|
66
|
+
...expanded,
|
|
67
|
+
...direct
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
var REMOVAL_PROBE = new Map(Object.entries({
|
|
71
|
+
padding: "0",
|
|
72
|
+
margin: "0",
|
|
73
|
+
inset: "0",
|
|
74
|
+
"border-radius": "0 0 0 0 / 0 0 0 0",
|
|
75
|
+
gap: "0 0",
|
|
76
|
+
"border-width": "0 0 0 0",
|
|
77
|
+
"border-color": "red red red red",
|
|
78
|
+
"border-style": "solid solid solid solid",
|
|
79
|
+
border: "0 solid red",
|
|
80
|
+
overflow: "visible visible",
|
|
81
|
+
flex: "0 0 auto",
|
|
82
|
+
"flex-flow": "row nowrap",
|
|
83
|
+
"place-content": "start start",
|
|
84
|
+
"place-items": "start start",
|
|
85
|
+
"place-self": "start start",
|
|
86
|
+
"grid-row": "auto / auto",
|
|
87
|
+
"grid-column": "auto / auto",
|
|
88
|
+
"grid-area": "auto / auto / auto / auto",
|
|
89
|
+
grid: "\"a\" 0 / 0",
|
|
90
|
+
"grid-template": "\"a\" 0 / 0",
|
|
91
|
+
columns: "0 0",
|
|
92
|
+
outline: "0 solid red",
|
|
93
|
+
"list-style": "disc inside url(x)",
|
|
94
|
+
"text-decoration": "underline solid red",
|
|
95
|
+
transition: "all 0s ease 0s",
|
|
96
|
+
animation: "a 0s ease 0s 1 normal forwards running",
|
|
97
|
+
background: "red url(x) no-repeat scroll 0 0 / auto padding-box border-box",
|
|
98
|
+
font: "italic small-caps bold 0px/0 a"
|
|
99
|
+
}));
|
|
100
|
+
for (const side of SIDES) REMOVAL_PROBE.set(`border-${side}`, "0 solid red");
|
|
101
|
+
/** The longhand keys a shorthand controls — what a `null` patch value has to clear. */
|
|
102
|
+
var shorthandLonghands = (key) => {
|
|
103
|
+
const probe = REMOVAL_PROBE.get(key);
|
|
104
|
+
if (probe === void 0) return;
|
|
105
|
+
const out = {};
|
|
106
|
+
expandOne(key, probe, out);
|
|
107
|
+
return Object.keys(out);
|
|
108
|
+
};
|
|
109
|
+
/** Patch flavour of {@link expandShorthand}: a `null` value removes every longhand the key controls, so
|
|
110
|
+
* `{ padding: null }` clears all four sides rather than a `padding` key that was never stored. */
|
|
111
|
+
var expandShorthandPatch = (css) => {
|
|
112
|
+
const expanded = {};
|
|
113
|
+
const direct = {};
|
|
114
|
+
for (const [key, value] of Object.entries(css)) {
|
|
115
|
+
if (value === null) {
|
|
116
|
+
const longhands = shorthandLonghands(key);
|
|
117
|
+
if (longhands) for (const longhand of longhands) expanded[longhand] = null;
|
|
118
|
+
else direct[key] = null;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
const props = {};
|
|
122
|
+
if (expandOne(key, value, props)) Object.assign(expanded, props);
|
|
123
|
+
else direct[key] = value;
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
...expanded,
|
|
127
|
+
...direct
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
//#endregion
|
|
131
|
+
export { cssProperties, cssShorthands, expandShorthand, expandShorthandPatch, isCssProperty, shorthandLonghands, suggestCssProperty };
|