@plitzi/sdk-server 0.32.5 → 0.32.6

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.
Files changed (168) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/core/services/mcp.js +1 -1
  3. package/dist/helpers/buildServerInfo.js +3 -9
  4. package/dist/index.js +2 -1
  5. package/dist/modules/mcp/catalogs/builtinCallbacks.js +204 -0
  6. package/dist/modules/mcp/catalogs/builtinComponents.js +170 -0
  7. package/dist/modules/mcp/catalogs/builtinElementCallbacks.js +50 -0
  8. package/dist/modules/mcp/catalogs/builtinTransformers.js +148 -0
  9. package/dist/modules/mcp/catalogs/builtinUtilities.js +90 -0
  10. package/dist/modules/mcp/{tools/operations/style → catalogs}/cssCatalog.js +75 -2
  11. package/dist/modules/mcp/catalogs/observed.js +90 -0
  12. package/dist/modules/mcp/catalogs/paramSpec.js +69 -0
  13. package/dist/modules/mcp/catalogs/registry.js +89 -0
  14. package/dist/modules/mcp/handler.js +12 -2
  15. package/dist/modules/mcp/helpers/agentPrompt.js +104 -0
  16. package/dist/modules/mcp/helpers/guide.js +334 -15
  17. package/dist/modules/mcp/helpers/interactions.js +123 -0
  18. package/dist/modules/mcp/helpers/log.js +54 -0
  19. package/dist/modules/mcp/helpers/space.js +158 -33
  20. package/dist/modules/mcp/helpers/uris.js +44 -0
  21. package/dist/modules/mcp/resources/canonical.js +6 -21
  22. package/dist/modules/mcp/resources/core.js +4 -3
  23. package/dist/modules/mcp/resources/primer.js +13 -8
  24. package/dist/modules/mcp/resources/register.js +37 -5
  25. package/dist/modules/mcp/resources/schema.js +30 -20
  26. package/dist/modules/mcp/resources/style.js +18 -11
  27. package/dist/modules/mcp/server.js +21 -7
  28. package/dist/modules/mcp/tools/apply/dispatch.js +38 -18
  29. package/dist/modules/mcp/tools/apply/index.js +19 -4
  30. package/dist/modules/mcp/tools/apply/writeResult.js +2 -1
  31. package/dist/modules/mcp/tools/index.js +1 -1
  32. package/dist/modules/mcp/tools/operations/index.js +13 -2
  33. package/dist/modules/mcp/tools/operations/schema/bindings/deleteBinding.js +32 -0
  34. package/dist/modules/mcp/tools/operations/schema/bindings/patchBinding.js +36 -0
  35. package/dist/modules/mcp/tools/operations/schema/bindings/upsertBinding.js +47 -0
  36. package/dist/modules/mcp/tools/operations/schema/{deleteElement.js → elements/deleteElement.js} +10 -6
  37. package/dist/modules/mcp/tools/operations/schema/{moveElement.js → elements/moveElement.js} +8 -6
  38. package/dist/modules/mcp/tools/operations/schema/{patchElement.js → elements/patchElement.js} +19 -7
  39. package/dist/modules/mcp/tools/operations/schema/{upsertElement.js → elements/upsertElement.js} +11 -5
  40. package/dist/modules/mcp/tools/operations/schema/{deleteFolder.js → folders/deleteFolder.js} +4 -4
  41. package/dist/modules/mcp/tools/operations/schema/{upsertFolder.js → folders/upsertFolder.js} +4 -4
  42. package/dist/modules/mcp/tools/operations/schema/interactions/deleteInteraction.js +52 -0
  43. package/dist/modules/mcp/tools/operations/schema/interactions/patchInteractionNode.js +47 -0
  44. package/dist/modules/mcp/tools/operations/schema/interactions/upsertInteractionFlow.js +43 -0
  45. package/dist/modules/mcp/tools/operations/schema/operations.js +25 -11
  46. package/dist/modules/mcp/tools/operations/schema/{deletePage.js → pages/deletePage.js} +8 -5
  47. package/dist/modules/mcp/tools/operations/schema/pages/upsertPage.js +75 -0
  48. package/dist/modules/mcp/tools/operations/schema/settings/patchSettings.js +45 -0
  49. package/dist/modules/mcp/tools/operations/schema/shared.js +94 -2
  50. package/dist/modules/mcp/tools/operations/schema/translator.js +77 -10
  51. package/dist/modules/mcp/tools/operations/schema/{deleteVariable.js → variables/deleteVariable.js} +3 -3
  52. package/dist/modules/mcp/tools/operations/schema/{upsertVariable.js → variables/upsertVariable.js} +4 -4
  53. package/dist/modules/mcp/tools/operations/schema/write.js +78 -13
  54. package/dist/modules/mcp/tools/operations/style/{deleteDefinition.js → definitions/deleteDefinition.js} +4 -3
  55. package/dist/modules/mcp/tools/operations/style/{patchDefinition.js → definitions/patchDefinition.js} +6 -5
  56. package/dist/modules/mcp/tools/operations/style/{upsertDefinition.js → definitions/upsertDefinition.js} +5 -4
  57. package/dist/modules/mcp/tools/operations/style/{deleteGlobalStyle.js → globalStyles/deleteGlobalStyle.js} +4 -3
  58. package/dist/modules/mcp/tools/operations/style/{patchGlobalStyle.js → globalStyles/patchGlobalStyle.js} +6 -5
  59. package/dist/modules/mcp/tools/operations/style/{upsertGlobalStyle.js → globalStyles/upsertGlobalStyle.js} +5 -4
  60. package/dist/modules/mcp/tools/operations/style/idStyles/deleteIdStyle.js +21 -0
  61. package/dist/modules/mcp/tools/operations/style/idStyles/patchIdStyle.js +28 -0
  62. package/dist/modules/mcp/tools/operations/style/idStyles/upsertIdStyle.js +24 -0
  63. package/dist/modules/mcp/tools/operations/style/operations.js +14 -8
  64. package/dist/modules/mcp/tools/operations/style/translator.js +25 -1
  65. package/dist/modules/mcp/tools/operations/style/{deleteStyleVariable.js → variables/deleteStyleVariable.js} +4 -4
  66. package/dist/modules/mcp/tools/operations/style/{upsertStyleVariable.js → variables/upsertStyleVariable.js} +4 -4
  67. package/dist/modules/mcp/tools/operations/style/write.js +19 -11
  68. package/dist/modules/mcp/tools/read.js +1 -1
  69. package/dist/modules/mcp/tools/search.js +34 -27
  70. package/dist/modules/mcp/tools/shared/validator/audit.js +124 -0
  71. package/dist/modules/mcp/tools/shared/validator/batch.js +30 -0
  72. package/dist/modules/mcp/tools/shared/validator/bindings.js +70 -0
  73. package/dist/modules/mcp/tools/shared/validator/context.js +35 -0
  74. package/dist/modules/mcp/tools/shared/validator/css.js +47 -0
  75. package/dist/modules/mcp/tools/shared/validator/elements.js +55 -0
  76. package/dist/modules/mcp/tools/shared/validator/index.js +226 -0
  77. package/dist/modules/mcp/tools/shared/validator/interactions.js +106 -0
  78. package/dist/modules/mcp/tools/shared/validator/refs.js +28 -0
  79. package/dist/modules/mcp/tools/validate.js +23 -3
  80. package/dist/modules/ssr/preview.js +1 -1
  81. package/dist/modules/ssr/views/template.ejs +2 -0
  82. package/dist/src/index.d.ts +1 -1
  83. package/dist/src/modules/mcp/catalogs/builtinCallbacks.d.ts +23 -0
  84. package/dist/src/modules/mcp/catalogs/builtinComponents.d.ts +4 -0
  85. package/dist/src/modules/mcp/catalogs/builtinElementCallbacks.d.ts +20 -0
  86. package/dist/src/modules/mcp/catalogs/builtinTransformers.d.ts +28 -0
  87. package/dist/src/modules/mcp/catalogs/builtinTransformers.test.d.ts +1 -0
  88. package/dist/src/modules/mcp/catalogs/builtinUtilities.d.ts +18 -0
  89. package/dist/src/modules/mcp/{tools/operations/style → catalogs}/cssCatalog.d.ts +5 -1
  90. package/dist/src/modules/mcp/catalogs/index.d.ts +9 -0
  91. package/dist/src/modules/mcp/catalogs/observed.d.ts +48 -0
  92. package/dist/src/modules/mcp/catalogs/paramSpec.d.ts +42 -0
  93. package/dist/src/modules/mcp/catalogs/registry.d.ts +35 -0
  94. package/dist/src/modules/mcp/handler.d.ts +2 -2
  95. package/dist/src/modules/mcp/helpers/agentPrompt.d.ts +4 -0
  96. package/dist/src/modules/mcp/helpers/guide.d.ts +2 -1
  97. package/dist/src/modules/mcp/helpers/index.d.ts +4 -0
  98. package/dist/src/modules/mcp/helpers/interactions.d.ts +30 -0
  99. package/dist/src/modules/mcp/helpers/log.d.ts +8 -0
  100. package/dist/src/modules/mcp/helpers/log.test.d.ts +1 -0
  101. package/dist/src/modules/mcp/helpers/space.d.ts +67 -11
  102. package/dist/src/modules/mcp/helpers/uris.d.ts +28 -0
  103. package/dist/src/modules/mcp/index.d.ts +1 -1
  104. package/dist/src/modules/mcp/mcp.bench.d.ts +1 -0
  105. package/dist/src/modules/mcp/resources/canonical.d.ts +0 -1
  106. package/dist/src/modules/mcp/resources/index.d.ts +2 -3
  107. package/dist/src/modules/mcp/resources/primer.d.ts +2 -1
  108. package/dist/src/modules/mcp/resources/register.d.ts +2 -2
  109. package/dist/src/modules/mcp/server.d.ts +5 -2
  110. package/dist/src/modules/mcp/tools/apply/index.d.ts +191 -2
  111. package/dist/src/modules/mcp/tools/operations/index.d.ts +383 -4
  112. package/dist/src/modules/mcp/tools/operations/schema/bindings/deleteBinding.d.ts +17 -0
  113. package/dist/src/modules/mcp/tools/operations/schema/bindings/patchBinding.d.ts +25 -0
  114. package/dist/src/modules/mcp/tools/operations/schema/bindings/upsertBinding.d.ts +27 -0
  115. package/dist/src/modules/mcp/tools/operations/schema/{deleteElement.d.ts → elements/deleteElement.d.ts} +2 -2
  116. package/dist/src/modules/mcp/tools/operations/schema/{moveElement.d.ts → elements/moveElement.d.ts} +2 -2
  117. package/dist/src/modules/mcp/tools/operations/schema/{patchElement.d.ts → elements/patchElement.d.ts} +7 -2
  118. package/dist/src/modules/mcp/tools/operations/schema/{upsertElement.d.ts → elements/upsertElement.d.ts} +3 -3
  119. package/dist/src/modules/mcp/tools/operations/schema/{deleteFolder.d.ts → folders/deleteFolder.d.ts} +2 -2
  120. package/dist/src/modules/mcp/tools/operations/schema/{upsertFolder.d.ts → folders/upsertFolder.d.ts} +2 -2
  121. package/dist/src/modules/mcp/tools/operations/schema/index.d.ts +17 -11
  122. package/dist/src/modules/mcp/tools/operations/schema/interactions/deleteInteraction.d.ts +12 -0
  123. package/dist/src/modules/mcp/tools/operations/schema/interactions/patchInteractionNode.d.ts +18 -0
  124. package/dist/src/modules/mcp/tools/operations/schema/interactions/upsertInteractionFlow.d.ts +27 -0
  125. package/dist/src/modules/mcp/tools/operations/schema/operations.d.ts +131 -0
  126. package/dist/src/modules/mcp/tools/operations/schema/{deletePage.d.ts → pages/deletePage.d.ts} +2 -2
  127. package/dist/src/modules/mcp/tools/operations/schema/{upsertPage.d.ts → pages/upsertPage.d.ts} +3 -2
  128. package/dist/src/modules/mcp/tools/operations/schema/settings/patchSettings.d.ts +34 -0
  129. package/dist/src/modules/mcp/tools/operations/schema/shared.d.ts +53 -0
  130. package/dist/src/modules/mcp/tools/operations/schema/translator.d.ts +5 -2
  131. package/dist/src/modules/mcp/tools/operations/schema/{deleteVariable.d.ts → variables/deleteVariable.d.ts} +2 -2
  132. package/dist/src/modules/mcp/tools/operations/schema/{upsertVariable.d.ts → variables/upsertVariable.d.ts} +2 -2
  133. package/dist/src/modules/mcp/tools/operations/schema/write.d.ts +38 -8
  134. package/dist/src/modules/mcp/tools/operations/style/{deleteDefinition.d.ts → definitions/deleteDefinition.d.ts} +2 -2
  135. package/dist/src/modules/mcp/tools/operations/style/{patchDefinition.d.ts → definitions/patchDefinition.d.ts} +2 -2
  136. package/dist/src/modules/mcp/tools/operations/style/{upsertDefinition.d.ts → definitions/upsertDefinition.d.ts} +2 -2
  137. package/dist/src/modules/mcp/tools/operations/style/{deleteGlobalStyle.d.ts → globalStyles/deleteGlobalStyle.d.ts} +2 -2
  138. package/dist/src/modules/mcp/tools/operations/style/{patchGlobalStyle.d.ts → globalStyles/patchGlobalStyle.d.ts} +2 -2
  139. package/dist/src/modules/mcp/tools/operations/style/{upsertGlobalStyle.d.ts → globalStyles/upsertGlobalStyle.d.ts} +2 -2
  140. package/dist/src/modules/mcp/tools/operations/style/idStyles/deleteIdStyle.d.ts +9 -0
  141. package/dist/src/modules/mcp/tools/operations/style/idStyles/patchIdStyle.d.ts +37 -0
  142. package/dist/src/modules/mcp/tools/operations/style/idStyles/upsertIdStyle.d.ts +37 -0
  143. package/dist/src/modules/mcp/tools/operations/style/index.d.ts +11 -9
  144. package/dist/src/modules/mcp/tools/operations/style/operations.d.ts +70 -2
  145. package/dist/src/modules/mcp/tools/operations/style/shared.d.ts +1 -1
  146. package/dist/src/modules/mcp/tools/operations/style/translator.d.ts +4 -1
  147. package/dist/src/modules/mcp/tools/operations/style/{deleteStyleVariable.d.ts → variables/deleteStyleVariable.d.ts} +3 -3
  148. package/dist/src/modules/mcp/tools/operations/style/{upsertStyleVariable.d.ts → variables/upsertStyleVariable.d.ts} +3 -3
  149. package/dist/src/modules/mcp/tools/operations/style/write.d.ts +2 -7
  150. package/dist/src/modules/mcp/tools/preview.d.ts +191 -2
  151. package/dist/src/modules/mcp/tools/screenshot.d.ts +191 -2
  152. package/dist/src/modules/mcp/tools/shared/validator/audit.d.ts +4 -0
  153. package/dist/src/modules/mcp/tools/shared/validator/batch.d.ts +5 -0
  154. package/dist/src/modules/mcp/tools/shared/validator/bindings.d.ts +6 -0
  155. package/dist/src/modules/mcp/tools/shared/validator/context.d.ts +34 -0
  156. package/dist/src/modules/mcp/tools/shared/validator/css.d.ts +4 -0
  157. package/dist/src/modules/mcp/tools/shared/validator/elements.d.ts +6 -0
  158. package/dist/src/modules/mcp/tools/shared/validator/index.d.ts +8 -0
  159. package/dist/src/modules/mcp/tools/shared/validator/interactions.d.ts +3 -0
  160. package/dist/src/modules/mcp/tools/shared/validator/refs.d.ts +4 -0
  161. package/dist/src/modules/mcp/tools/validate.d.ts +191 -2
  162. package/dist/src/modules/mcp/types/aiSchema.d.ts +93 -1
  163. package/package.json +12 -10
  164. package/dist/modules/mcp/tools/operations/schema/registry.js +0 -55
  165. package/dist/modules/mcp/tools/operations/schema/upsertPage.js +0 -64
  166. package/dist/modules/mcp/tools/shared/validator.js +0 -262
  167. package/dist/src/modules/mcp/tools/operations/schema/registry.d.ts +0 -22
  168. package/dist/src/modules/mcp/tools/shared/validator.d.ts +0 -4
@@ -20,11 +20,14 @@ export interface AIPageSummary {
20
20
  label: string;
21
21
  slug: string;
22
22
  default: boolean;
23
+ /** Whether the page is served by the published SDK runtime. A disabled page (`false`) is not routable/accessible
24
+ * to end users, but stays fully editable here. Defaults to true. */
25
+ enabled: boolean;
23
26
  /** The ref of the folder this page lives in (a PageFolder id), or undefined for a root-level page. */
24
27
  folder?: string;
25
28
  elementCount: number;
26
29
  }
27
- /** A page folder in the sidebar tree. `ref` is the folder's id (there is no separate aiRef); pages reference it
30
+ /** A page folder in the sidebar tree. `ref` is the folder's id (there is no separate idRef); pages reference it
28
31
  * by that id via their `folder`, and nested folders via `parentId`. */
29
32
  export interface AIFolder {
30
33
  ref: string;
@@ -37,6 +40,9 @@ export interface AISkeletonNode {
37
40
  type: string;
38
41
  label: string;
39
42
  subType?: string;
43
+ /** This element's version — identical to the stateVersion a direct element read or search hit returns for it. Diff
44
+ * it against a version you already hold to re-read (plitzi_read) only the nodes that actually changed. */
45
+ stateVersion?: string;
40
46
  /** The class refs this node attaches on its base selector — names only, no CSS. Lets an agent map element →
41
47
  * class straight from the page skeleton, without a per-element read just to learn which class it uses. */
42
48
  base?: string[];
@@ -50,6 +56,9 @@ export interface AIPageSkeleton {
50
56
  label: string;
51
57
  slug: string;
52
58
  default: boolean;
59
+ /** Whether the page is served by the published SDK runtime. A disabled page (`false`) is not routable/accessible
60
+ * to end users, but stays fully editable here. Defaults to true. */
61
+ enabled: boolean;
53
62
  /** Route params bound by the slug (e.g. ":spaceId" → ["spaceId"]). Valid as {{name}} references on this page,
54
63
  * alongside the space-level plitzi://schema-variables. */
55
64
  routeParams: string[];
@@ -63,6 +72,53 @@ export interface AIPageStyles {
63
72
  definitions: AIDefinition[];
64
73
  globalStyles: AIGlobalStyle[];
65
74
  }
75
+ /** Which variant each attached class/selector currently uses on this element (element.definition.initialState
76
+ * .styleVariant). Outer key = class ref, inner key = selector (`base` or a slot), value = variant name(s). */
77
+ export type AIStyleVariantSelection = Record<string, Record<string, string | string[]>>;
78
+ /** The element's initial (default) state overrides. `styleVariant` picks which variant of its classes is active;
79
+ * `visibility` hides/shows it initially. Kept minimal — the two fields agents actually set. */
80
+ export interface AIInitialState {
81
+ styleVariant?: AIStyleVariantSelection;
82
+ visibility?: boolean;
83
+ }
84
+ /** One data binding: connect a data `source` to the element field named by `to` (a prop, a style value, or an
85
+ * initial-state key, per its category). `transformers` reshape the value; `when` gates the binding. */
86
+ export interface AIBinding {
87
+ id: string;
88
+ to: string;
89
+ source: string;
90
+ transformers?: Array<{
91
+ action: string;
92
+ params: Record<string, string>;
93
+ enabled?: boolean;
94
+ }>;
95
+ when?: unknown;
96
+ enabled?: boolean;
97
+ }
98
+ /** Data bindings on an element, grouped by what they feed: element props (`attributes`), style values (`style`),
99
+ * or initial-state keys (`initialState`). */
100
+ export type AIBindings = Partial<Record<'attributes' | 'style' | 'initialState', AIBinding[]>>;
101
+ export type AIInteractionNodeType = 'trigger' | 'globalCallback' | 'callback' | 'utility';
102
+ /** One step of an interaction flow, projected from the stored doubly-linked node. Order is conveyed by the
103
+ * position in `AIInteractionFlow.nodes`; the stored beforeNode/afterNode/flowId links are computed on write. */
104
+ export interface AIInteractionNode {
105
+ id: string;
106
+ title: string;
107
+ nodeType: AIInteractionNodeType;
108
+ action: string;
109
+ params?: Record<string, unknown>;
110
+ enabled?: boolean;
111
+ when?: unknown;
112
+ /** Source element the callback targets (globalCallback/utility). Defaults to this element on write. */
113
+ elementId?: string;
114
+ preview?: Record<string, unknown>;
115
+ }
116
+ /** One interaction flow on an element: a trigger (first node) followed by the callbacks/utilities it runs, in
117
+ * order. `flowId` equals the trigger node id. */
118
+ export interface AIInteractionFlow {
119
+ flowId: string;
120
+ nodes: AIInteractionNode[];
121
+ }
66
122
  export interface AIElementDetail {
67
123
  ref: string;
68
124
  type: string;
@@ -81,6 +137,18 @@ export interface AIElementDetail {
81
137
  /** Global (type 'element') styles that also affect this element because they target its type — every element of
82
138
  * the type inherits them. Read-only here (not editable as definitions); shown so the effective CSS is complete. */
83
139
  globalStyles?: AIGlobalStyle[];
140
+ /** Id rule (type 'id') targeting this element by its DOM `id` attribute (`#id`). Present only when the element
141
+ * carries an `id` that a rule matches. Edit it with the id-style tools (never as a definition). */
142
+ idStyle?: AIIdStyle;
143
+ /** Variant names each attached class exposes (deduped across its selectors), so the agent knows a class HAS a
144
+ * variant (e.g. a button class with "primary") before applying it via `initialState.styleVariant`. */
145
+ availableVariants?: Record<string, string[]>;
146
+ /** Which variant/visibility this element applies today (element.definition.initialState). */
147
+ initialState?: AIInitialState;
148
+ /** Data bindings on this element, grouped by category. */
149
+ bindings?: AIBindings;
150
+ /** Interaction flows on this element (event → ordered callbacks). */
151
+ interactions?: AIInteractionFlow[];
84
152
  childRefs?: string[];
85
153
  }
86
154
  /** A global element style (a type 'element' StyleItem): its CSS applies to every element whose type equals
@@ -88,6 +156,12 @@ export interface AIElementDetail {
88
156
  export interface AIGlobalStyle extends AIDefinition {
89
157
  appliesToType: string;
90
158
  }
159
+ /** An id rule (a type 'id' StyleItem): its CSS targets the single element whose DOM `id` equals `targetId`
160
+ * (the CSS equivalent of `#id { … }`). Style one element by giving it an `id` attribute and writing this rule;
161
+ * prefer a class definition when the styling could be reused. */
162
+ export interface AIIdStyle extends AIDefinition {
163
+ targetId: string;
164
+ }
91
165
  export interface AIDefinitionSlot extends DisplayModeCss {
92
166
  states?: Record<string, DisplayModeCss>;
93
167
  variants?: Record<string, DisplayModeCss>;
@@ -118,6 +192,24 @@ export interface AISchemaVariable {
118
192
  value: string | number | boolean;
119
193
  }>;
120
194
  }
195
+ /** Space-level settings: the arbitrary global CSS (`customCss`), state persistence, and the user/auth provider
196
+ * configuration. Every field is optional — a patch changes only the keys it sends. */
197
+ export interface AISettings {
198
+ customCss?: string;
199
+ keepState?: boolean;
200
+ stateStorage?: 'localStorage' | 'sessionStorage';
201
+ userProvider?: 'auth0' | 'basic' | 'custom' | '';
202
+ auth0Domain?: string;
203
+ auth0ClientId?: string;
204
+ tokenStorage?: 'localStorage' | 'sessionStorage' | '';
205
+ loginUrl?: string;
206
+ userUrl?: string;
207
+ refreshUrl?: string;
208
+ logoutUrl?: string;
209
+ detailsPath?: string;
210
+ tokenPath?: string;
211
+ expirationTimePath?: string;
212
+ }
121
213
  export interface ValidationError {
122
214
  path: string;
123
215
  message: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plitzi/sdk-server",
3
- "version": "0.32.5",
3
+ "version": "0.32.6",
4
4
  "license": "AGPL-3.0",
5
5
  "files": [
6
6
  "dist"
@@ -23,8 +23,9 @@
23
23
  "sideEffects": false,
24
24
  "dependencies": {
25
25
  "@modelcontextprotocol/sdk": "^1.29.0",
26
- "@plitzi/plitzi-sdk": "0.32.5",
27
- "@plitzi/sdk-shared": "0.32.5",
26
+ "@plitzi/plitzi-sdk": "0.32.6",
27
+ "@plitzi/sdk-schema": "0.32.6",
28
+ "@plitzi/sdk-shared": "0.32.6",
28
29
  "ejs": "^6.0.1",
29
30
  "esbuild": "^0.28.1",
30
31
  "zod": "^4.4.3"
@@ -37,15 +38,15 @@
37
38
  "@types/ejs": "^3.1.5",
38
39
  "@types/node": "^26.1.1",
39
40
  "@types/react": "^19.2.17",
40
- "@vitejs/plugin-react": "^6.0.3",
41
+ "@vitejs/plugin-react": "^6.0.4",
41
42
  "@vitest/coverage-v8": "^4.1.10",
42
- "eslint": "^9.39.4",
43
+ "eslint": "^9.39.5",
43
44
  "eslint-plugin-import": "^2.32.0",
44
- "react": "^19.2.7",
45
- "react-dom": "^19.2.7",
46
- "tsx": "^4.23.0",
45
+ "react": "^19.2.8",
46
+ "react-dom": "^19.2.8",
47
+ "tsx": "^4.23.1",
47
48
  "typescript": "^6.0.3",
48
- "vite": "^8.1.4",
49
+ "vite": "^8.1.5",
49
50
  "vite-plugin-dts": "^5.0.3",
50
51
  "vitest": "^4.1.10"
51
52
  },
@@ -59,6 +60,7 @@
59
60
  "lint": "TIMING=1 eslint ./src",
60
61
  "typecheck": "tsc -p tsconfig.app.json --noEmit",
61
62
  "test": "vitest run",
62
- "test:coverage": "vitest run --coverage"
63
+ "test:coverage": "vitest run --coverage",
64
+ "bench": "vitest bench --run"
63
65
  }
64
66
  }
@@ -1,55 +0,0 @@
1
- import { cssProperties } from "../style/cssCatalog.js";
2
- //#region src/modules/mcp/tools/operations/schema/registry.ts
3
- var EXAMPLE_MAX_LENGTH = 80;
4
- var isCompactExample = (value) => {
5
- if (typeof value === "string") return value.length <= EXAMPLE_MAX_LENGTH;
6
- return typeof value === "number" || typeof value === "boolean";
7
- };
8
- var NOTE = "Element types and their props/slots are observed from the elements that already exist in this space — they are ground truth, never inferred. Props list the attribute keys seen on each type with example values. Slots are the styleSelectors keys seen on each type (target them via element.style.slots). CSS in definitions must use the kebab-case keys in cssProperties. Reference schema variables in props via {{name}} and style variables in CSS via var(--name).";
9
- var buildTypeRegistry = (schema) => {
10
- const types = {};
11
- for (const el of Object.values(schema.flat)) {
12
- const typeName = el.definition.type;
13
- const info = types[typeName] ??= {
14
- count: 0,
15
- subTypes: [],
16
- slots: [],
17
- props: {}
18
- };
19
- info.count += 1;
20
- const subType = el.attributes.subType;
21
- if (typeof subType === "string" && !info.subTypes.includes(subType)) info.subTypes.push(subType);
22
- for (const slot of Object.keys(el.definition.styleSelectors)) if (!info.slots.includes(slot)) info.slots.push(slot);
23
- for (const [key, value] of Object.entries(el.attributes)) {
24
- if (key === "subType") continue;
25
- const prop = info.props[key] ??= {
26
- valueTypes: [],
27
- examples: []
28
- };
29
- const valueType = value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
30
- if (!prop.valueTypes.includes(valueType)) prop.valueTypes.push(valueType);
31
- if (prop.examples.length < 3 && isCompactExample(value) && value !== "" && !prop.examples.includes(value)) prop.examples.push(value);
32
- }
33
- }
34
- for (const info of Object.values(types)) {
35
- info.subTypes.sort();
36
- info.slots.sort();
37
- }
38
- return {
39
- note: NOTE,
40
- cssProperties,
41
- styleVariableCategories: [
42
- "color",
43
- "spacing",
44
- "shadow",
45
- "custom"
46
- ],
47
- templateSyntax: {
48
- schemaVariable: "{{name}}",
49
- styleVariable: "var(--name)"
50
- },
51
- types
52
- };
53
- };
54
- //#endregion
55
- export { buildTypeRegistry };
@@ -1,64 +0,0 @@
1
- import { findFolderByRef, findPageByRef, generateObjectId } from "../../../helpers/space.js";
2
- import { empty, fail } from "../../../helpers/opResult.js";
3
- import { pageUri, pagesUri } from "./write.js";
4
- import { z } from "zod";
5
- //#region src/modules/mcp/tools/operations/schema/upsertPage.ts
6
- var upsertPageOp = z.object({
7
- type: z.literal("upsertPage"),
8
- ref: z.string().describe("Page id/slug to update, or a new id you choose to create one"),
9
- label: z.string().optional(),
10
- slug: z.string().optional(),
11
- folder: z.string().nullable().optional().describe("Ref of an existing folder to place this page in; \"\" or null moves it to the root. Unknown → error"),
12
- default: z.boolean().optional()
13
- }).describe("Create a page, or update it when ref already exists (only the fields you pass change).");
14
- var upsertPage = (space, env, op) => {
15
- let folderValue;
16
- if (op.folder !== void 0) if (op.folder === null || op.folder === "") folderValue = "";
17
- else {
18
- const resolved = findFolderByRef(space.schema, op.folder);
19
- if (!resolved) return fail("folder", `Folder "${op.folder}" not found`, "Create it with upsertFolder, or read plitzi://folders");
20
- folderValue = resolved.id;
21
- }
22
- const existing = findPageByRef(space.schema, op.ref);
23
- if (existing) {
24
- existing.attributes = {
25
- ...existing.attributes,
26
- ...op.slug !== void 0 ? { slug: op.slug } : {},
27
- ...op.label !== void 0 ? { name: op.label } : {},
28
- ...op.default !== void 0 ? { default: op.default } : {},
29
- ...folderValue !== void 0 ? { folder: folderValue } : {}
30
- };
31
- return {
32
- ...empty(),
33
- updated: 1,
34
- staleResources: [pageUri(env, op.ref), pagesUri(env)]
35
- };
36
- }
37
- const id = generateObjectId();
38
- const attributes = {
39
- slug: op.slug ?? "",
40
- name: op.label ?? op.ref,
41
- default: op.default ?? false,
42
- folder: folderValue ?? ""
43
- };
44
- space.schema.flat[id] = {
45
- id,
46
- attributes,
47
- definition: {
48
- rootId: id,
49
- label: op.label ?? op.ref,
50
- type: "page",
51
- items: [],
52
- styleSelectors: { base: "" },
53
- aiRef: op.ref
54
- }
55
- };
56
- space.schema.pages.push(id);
57
- return {
58
- ...empty(),
59
- created: 1,
60
- staleResources: [pageUri(env, op.ref), pagesUri(env)]
61
- };
62
- };
63
- //#endregion
64
- export { upsertPage, upsertPageOp };
@@ -1,262 +0,0 @@
1
- import { findFolderByRef, findPageByRef, folderAncestorIds, getPageElements, pageFoldersOf, pageRefOf, resolveRef, routeParamNames, slugRouteParams } from "../../helpers/space.js";
2
- import { expandShorthand, isCssProperty, suggestCssProperty } from "../operations/style/cssCatalog.js";
3
- import { buildTypeRegistry } from "../operations/schema/registry.js";
4
- //#region src/modules/mcp/tools/shared/validator.ts
5
- var REF_RE = /^[a-zA-Z0-9._-]+$/;
6
- var MAX_OPS = 100;
7
- var STYLE_CATEGORIES = [
8
- "color",
9
- "spacing",
10
- "shadow",
11
- "custom"
12
- ];
13
- var VAR_REF = /\{\{\s*([A-Za-z_][\w.-]*)\s*\}\}/g;
14
- var CSS_VAR = /var\(\s*--([A-Za-z_][\w-]*)\s*\)/g;
15
- var RAW_CODE_TYPES = /* @__PURE__ */ new Set([
16
- "blockJsx",
17
- "blockHtml",
18
- "custom"
19
- ]);
20
- var warnOnce = (ctx, message) => {
21
- if (!ctx.warned.has(message)) {
22
- ctx.warned.add(message);
23
- ctx.warnings.push(message);
24
- }
25
- };
26
- var checkVarRefs = (text, path, ctx) => {
27
- for (const match of text.matchAll(VAR_REF)) {
28
- const name = match[1];
29
- if (!ctx.schemaVars.has(name)) warnOnce(ctx, `Unknown variable {{${name}}} at ${path}: not a space schema variable or a page route param. Read plitzi://schema-variables, or use a route param from the page skeleton (routeParams).`);
30
- }
31
- };
32
- var checkStyleVarRefs = (text, path, ctx) => {
33
- for (const match of text.matchAll(CSS_VAR)) {
34
- const name = match[1];
35
- if (!ctx.styleVars.has(name)) {
36
- const tokens = [...ctx.styleVars];
37
- warnOnce(ctx, `Unknown style variable var(--${name}) at ${path}: not a design token in this space. ${tokens.length > 0 ? `Available tokens: ${tokens.slice(0, 30).join(", ")}${tokens.length > 30 ? ", …" : ""}.` : "Read plitzi://style-variables for the valid token names."}`);
38
- }
39
- }
40
- };
41
- var checkTypeProps = (type, props, path, ctx) => {
42
- if (!props || RAW_CODE_TYPES.has(type)) return;
43
- const known = ctx.typeProps.get(type);
44
- if (!known || known.size === 0) return;
45
- for (const key of Object.keys(props)) {
46
- if (key === "subType" || known.has(key)) continue;
47
- warnOnce(ctx, `Type "${type}" has no observed prop "${key}" at ${path} (observed: ${[...known].sort().join(", ")}). It may still be valid — verify against plitzi://types.`);
48
- }
49
- };
50
- var checkRef = (ref, path, ctx) => {
51
- if (!ref || ref.trim().length === 0) {
52
- ctx.errors.push({
53
- path,
54
- message: "Ref must not be empty",
55
- hint: "Use a semantic name like \"hero.title\""
56
- });
57
- return;
58
- }
59
- if (!REF_RE.test(ref)) ctx.errors.push({
60
- path,
61
- message: `Ref "${ref}" has invalid characters`,
62
- hint: "Allowed characters: letters, numbers, dot, hyphen, underscore"
63
- });
64
- };
65
- var checkCss = (css, path, ctx) => {
66
- if (!css) return;
67
- const declared = {};
68
- for (const [key, value] of Object.entries(css)) if (value !== null) declared[key] = value;
69
- for (const [key, value] of Object.entries(expandShorthand(declared))) {
70
- if (!isCssProperty(key)) {
71
- const suggestion = suggestCssProperty(key);
72
- ctx.errors.push({
73
- path: `${path}.${key}`,
74
- message: `Unknown CSS property "${key}"`,
75
- hint: suggestion ? `Use the kebab-case key "${suggestion}"` : "Read plitzi://css-properties for the valid property keys"
76
- });
77
- }
78
- if (typeof value === "string") {
79
- checkStyleVarRefs(value, `${path}.${key}`, ctx);
80
- checkVarRefs(value, `${path}.${key}`, ctx);
81
- }
82
- }
83
- };
84
- var checkSlotCss = (slot, path, ctx) => {
85
- checkCss(slot.desktop, `${path}.desktop`, ctx);
86
- checkCss(slot.tablet, `${path}.tablet`, ctx);
87
- checkCss(slot.mobile, `${path}.mobile`, ctx);
88
- for (const [state, dm] of Object.entries(slot.states ?? {})) {
89
- checkCss(dm.desktop, `${path}.states.${state}.desktop`, ctx);
90
- checkCss(dm.tablet, `${path}.states.${state}.tablet`, ctx);
91
- checkCss(dm.mobile, `${path}.states.${state}.mobile`, ctx);
92
- }
93
- for (const [name, dm] of Object.entries(slot.variants ?? {})) {
94
- checkCss(dm.desktop, `${path}.variants.${name}.desktop`, ctx);
95
- checkCss(dm.tablet, `${path}.variants.${name}.tablet`, ctx);
96
- checkCss(dm.mobile, `${path}.variants.${name}.mobile`, ctx);
97
- }
98
- };
99
- var checkElementProps = (element, path, ctx) => {
100
- if (!element.props || RAW_CODE_TYPES.has(element.type)) return;
101
- for (const [key, value] of Object.entries(element.props)) if (typeof value === "string") checkVarRefs(value, `${path}.props.${key}`, ctx);
102
- checkTypeProps(element.type, element.props, path, ctx);
103
- };
104
- var checkElementInput = (element, path, ctx, seen) => {
105
- checkRef(element.ref, `${path}.ref`, ctx);
106
- if (seen.has(element.ref)) ctx.errors.push({
107
- path: `${path}.ref`,
108
- message: `Duplicate ref "${element.ref}" in this batch`,
109
- hint: "Use a unique ref"
110
- });
111
- seen.add(element.ref);
112
- if (!element.type) ctx.errors.push({
113
- path: `${path}.type`,
114
- message: "Element type is required",
115
- hint: "Read plitzi://types for known types"
116
- });
117
- else if (!ctx.knownTypes.has(element.type)) ctx.warnings.push(`Type "${element.type}" was not seen in this space; ensure a plugin provides it (${path}.type).`);
118
- checkElementProps(element, path, ctx);
119
- element.children?.forEach((child, i) => checkElementInput(child, `${path}.children[${i}]`, ctx, seen));
120
- };
121
- var batchDeclaredVars = (ops) => {
122
- const names = [];
123
- for (const op of ops) if (op.type === "upsertVariable") names.push(op.name);
124
- else if (op.type === "upsertPage" && typeof op.slug === "string") names.push(...slugRouteParams(op.slug));
125
- return names;
126
- };
127
- var batchDeclaredPages = (ops) => {
128
- const refs = /* @__PURE__ */ new Set();
129
- for (const op of ops) if (op.type === "upsertPage") refs.add(op.ref);
130
- return refs;
131
- };
132
- var batchDeclaredFolders = (ops) => {
133
- const refs = /* @__PURE__ */ new Set();
134
- for (const op of ops) if (op.type === "upsertFolder") refs.add(op.ref);
135
- return refs;
136
- };
137
- var validateOperations = (space, ops) => {
138
- const registry = buildTypeRegistry(space.schema);
139
- const batchPages = batchDeclaredPages(ops);
140
- const batchFolders = batchDeclaredFolders(ops);
141
- const folderRefs = () => pageFoldersOf(space.schema).map((f) => f.id);
142
- const ctx = {
143
- errors: [],
144
- warnings: [],
145
- warned: /* @__PURE__ */ new Set(),
146
- knownTypes: new Set(Object.keys(registry.types)),
147
- typeProps: new Map(Object.entries(registry.types).map(([type, info]) => [type, new Set(Object.keys(info.props))])),
148
- schemaVars: /* @__PURE__ */ new Set([
149
- ...space.schema.variables.map((v) => v.name),
150
- ...routeParamNames(space.schema),
151
- ...batchDeclaredVars(ops)
152
- ]),
153
- styleVars: new Set(Object.values(space.style.variables).flatMap((group) => Object.keys(group)))
154
- };
155
- if (ops.length > MAX_OPS) ctx.errors.push({
156
- path: "operations",
157
- message: `Batch has ${ops.length} operations (max ${MAX_OPS})`,
158
- hint: `Split into batches of at most ${MAX_OPS}`
159
- });
160
- ops.forEach((op, i) => {
161
- const base = `operations[${i}]`;
162
- if ((op.type === "upsertElement" || op.type === "patchElement" || op.type === "deleteElement" || op.type === "moveElement") && op.pageRef) {
163
- if (!findPageByRef(space.schema, op.pageRef) && !batchPages.has(op.pageRef)) {
164
- const validRefs = getPageElements(space.schema).map(pageRefOf);
165
- ctx.errors.push({
166
- path: `${base}.pageRef`,
167
- message: `Page "${op.pageRef}" does not exist`,
168
- hint: "Use an existing page ref, or create it with upsertPage earlier in the same batch",
169
- validValues: validRefs
170
- });
171
- }
172
- }
173
- switch (op.type) {
174
- case "upsertElement":
175
- checkElementInput(op.element, `${base}.element`, ctx, /* @__PURE__ */ new Set());
176
- break;
177
- case "patchElement": {
178
- checkRef(op.ref, `${base}.ref`, ctx);
179
- const page = findPageByRef(space.schema, op.pageRef);
180
- const target = page ? resolveRef(space.schema, page, op.ref) : void 0;
181
- if (op.props) {
182
- for (const [key, value] of Object.entries(op.props)) if (typeof value === "string") checkVarRefs(value, `${base}.props.${key}`, ctx);
183
- if (target && target.id !== page?.id) checkTypeProps(target.definition.type, op.props, base, ctx);
184
- }
185
- break;
186
- }
187
- case "upsertDefinition":
188
- case "patchDefinition": {
189
- const { type, ref, slots, ...slot } = op;
190
- checkRef(ref, `${base}.ref`, ctx);
191
- checkSlotCss(slot, base, ctx);
192
- for (const [slotName, slotDef] of Object.entries(slots ?? {})) checkSlotCss(slotDef, `${base}.slots.${slotName}`, ctx);
193
- break;
194
- }
195
- case "upsertGlobalStyle":
196
- case "patchGlobalStyle": {
197
- const { type, componentType, slots, ...slot } = op;
198
- checkRef(componentType, `${base}.componentType`, ctx);
199
- checkSlotCss(slot, base, ctx);
200
- for (const [slotName, slotDef] of Object.entries(slots ?? {})) checkSlotCss(slotDef, `${base}.slots.${slotName}`, ctx);
201
- break;
202
- }
203
- case "deleteGlobalStyle":
204
- checkRef(op.componentType, `${base}.componentType`, ctx);
205
- break;
206
- case "upsertPage":
207
- checkRef(op.ref, `${base}.ref`, ctx);
208
- if (typeof op.folder === "string" && op.folder !== "") {
209
- if (!findFolderByRef(space.schema, op.folder) && !batchFolders.has(op.folder)) ctx.errors.push({
210
- path: `${base}.folder`,
211
- message: `Folder "${op.folder}" does not exist`,
212
- hint: "Create it with upsertFolder earlier in the same batch, or read plitzi://folders for valid refs",
213
- validValues: folderRefs()
214
- });
215
- }
216
- break;
217
- case "upsertFolder":
218
- checkRef(op.ref, `${base}.ref`, ctx);
219
- if (typeof op.parentId === "string") {
220
- checkRef(op.parentId, `${base}.parentId`, ctx);
221
- const parent = findFolderByRef(space.schema, op.parentId);
222
- if (!parent && !batchFolders.has(op.parentId)) ctx.errors.push({
223
- path: `${base}.parentId`,
224
- message: `Parent folder "${op.parentId}" does not exist`,
225
- hint: "Create the parent with upsertFolder first, or read plitzi://folders for valid refs",
226
- validValues: folderRefs()
227
- });
228
- const selfId = findFolderByRef(space.schema, op.ref)?.id ?? op.ref;
229
- if ((parent?.id ?? op.parentId) === selfId || parent && folderAncestorIds(pageFoldersOf(space.schema), parent.id).includes(selfId)) ctx.errors.push({
230
- path: `${base}.parentId`,
231
- message: `Folder "${op.ref}" cannot be nested under itself or one of its descendants`,
232
- hint: "Choose a parent that is not this folder or below it"
233
- });
234
- }
235
- break;
236
- case "deleteDefinition":
237
- case "deleteFolder":
238
- case "deletePage":
239
- case "upsertVariable":
240
- case "deleteVariable":
241
- checkRef("ref" in op ? op.ref : op.name, `${base}.${"ref" in op ? "ref" : "name"}`, ctx);
242
- break;
243
- case "upsertStyleVariable":
244
- case "deleteStyleVariable":
245
- if (!STYLE_CATEGORIES.includes(op.category)) ctx.errors.push({
246
- path: `${base}.category`,
247
- message: `Unknown style-variable category "${op.category}"`,
248
- hint: "Use one of the valid categories",
249
- validValues: STYLE_CATEGORIES
250
- });
251
- break;
252
- default: break;
253
- }
254
- });
255
- return {
256
- valid: ctx.errors.length === 0,
257
- errors: ctx.errors,
258
- warnings: ctx.warnings
259
- };
260
- };
261
- //#endregion
262
- export { validateOperations };
@@ -1,22 +0,0 @@
1
- import { Schema } from '@plitzi/sdk-shared';
2
- export interface TypePropInfo {
3
- valueTypes: string[];
4
- examples: unknown[];
5
- }
6
- export interface TypeInfo {
7
- count: number;
8
- subTypes: string[];
9
- slots: string[];
10
- props: Record<string, TypePropInfo>;
11
- }
12
- export interface TypeRegistry {
13
- note: string;
14
- cssProperties: string[];
15
- styleVariableCategories: string[];
16
- templateSyntax: {
17
- schemaVariable: string;
18
- styleVariable: string;
19
- };
20
- types: Record<string, TypeInfo>;
21
- }
22
- export declare const buildTypeRegistry: (schema: Schema) => TypeRegistry;
@@ -1,4 +0,0 @@
1
- import { Space } from '../../helpers';
2
- import { ValidationResult } from '../../types';
3
- import { Operation } from '../operations';
4
- export declare const validateOperations: (space: Space, ops: Operation[]) => ValidationResult;