fastscript 0.1.1 → 2.0.0

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 (108) hide show
  1. package/CHANGELOG.md +31 -2
  2. package/LICENSE +33 -21
  3. package/README.md +568 -59
  4. package/node_modules/@fastscript/core-private/BOUNDARY.json +15 -0
  5. package/node_modules/@fastscript/core-private/README.md +5 -0
  6. package/node_modules/@fastscript/core-private/package.json +34 -0
  7. package/node_modules/@fastscript/core-private/src/asset-optimizer.mjs +67 -0
  8. package/node_modules/@fastscript/core-private/src/audit-log.mjs +50 -0
  9. package/node_modules/@fastscript/core-private/src/auth-flows.mjs +29 -0
  10. package/node_modules/@fastscript/core-private/src/auth.mjs +115 -0
  11. package/node_modules/@fastscript/core-private/src/bench.mjs +45 -0
  12. package/node_modules/@fastscript/core-private/src/build.mjs +670 -0
  13. package/node_modules/@fastscript/core-private/src/cache.mjs +248 -0
  14. package/node_modules/@fastscript/core-private/src/check.mjs +22 -0
  15. package/node_modules/@fastscript/core-private/src/cli.mjs +95 -0
  16. package/node_modules/@fastscript/core-private/src/compat.mjs +128 -0
  17. package/node_modules/@fastscript/core-private/src/create.mjs +278 -0
  18. package/node_modules/@fastscript/core-private/src/csp.mjs +26 -0
  19. package/node_modules/@fastscript/core-private/src/db-cli.mjs +185 -0
  20. package/node_modules/@fastscript/core-private/src/db-postgres-collection.mjs +110 -0
  21. package/node_modules/@fastscript/core-private/src/db-postgres.mjs +40 -0
  22. package/node_modules/@fastscript/core-private/src/db.mjs +103 -0
  23. package/node_modules/@fastscript/core-private/src/deploy.mjs +662 -0
  24. package/node_modules/@fastscript/core-private/src/dev.mjs +5 -0
  25. package/node_modules/@fastscript/core-private/src/docs-search.mjs +35 -0
  26. package/node_modules/@fastscript/core-private/src/env.mjs +118 -0
  27. package/node_modules/@fastscript/core-private/src/export.mjs +83 -0
  28. package/node_modules/@fastscript/core-private/src/fs-diagnostics.mjs +70 -0
  29. package/node_modules/@fastscript/core-private/src/fs-error-codes.mjs +141 -0
  30. package/node_modules/@fastscript/core-private/src/fs-formatter.mjs +66 -0
  31. package/node_modules/@fastscript/core-private/src/fs-linter.mjs +274 -0
  32. package/node_modules/@fastscript/core-private/src/fs-normalize.mjs +91 -0
  33. package/node_modules/@fastscript/core-private/src/fs-parser.mjs +980 -0
  34. package/node_modules/@fastscript/core-private/src/generated/docs-search-index.mjs +3182 -0
  35. package/node_modules/@fastscript/core-private/src/i18n.mjs +25 -0
  36. package/node_modules/@fastscript/core-private/src/interop.mjs +16 -0
  37. package/node_modules/@fastscript/core-private/src/jobs.mjs +378 -0
  38. package/node_modules/@fastscript/core-private/src/logger.mjs +27 -0
  39. package/node_modules/@fastscript/core-private/src/metrics.mjs +45 -0
  40. package/node_modules/@fastscript/core-private/src/middleware.mjs +14 -0
  41. package/node_modules/@fastscript/core-private/src/migrate.mjs +81 -0
  42. package/node_modules/@fastscript/core-private/src/migration-wizard.mjs +16 -0
  43. package/node_modules/@fastscript/core-private/src/module-loader.mjs +46 -0
  44. package/node_modules/@fastscript/core-private/src/oauth-providers.mjs +103 -0
  45. package/node_modules/@fastscript/core-private/src/observability.mjs +21 -0
  46. package/node_modules/@fastscript/core-private/src/plugins.mjs +194 -0
  47. package/node_modules/@fastscript/core-private/src/retention.mjs +57 -0
  48. package/node_modules/@fastscript/core-private/src/routes.mjs +178 -0
  49. package/node_modules/@fastscript/core-private/src/scheduler.mjs +104 -0
  50. package/node_modules/@fastscript/core-private/src/security.mjs +233 -0
  51. package/node_modules/@fastscript/core-private/src/server-runtime.mjs +849 -0
  52. package/node_modules/@fastscript/core-private/src/serverless-handler.mjs +20 -0
  53. package/node_modules/@fastscript/core-private/src/session-policy.mjs +38 -0
  54. package/node_modules/@fastscript/core-private/src/start.mjs +10 -0
  55. package/node_modules/@fastscript/core-private/src/storage.mjs +155 -0
  56. package/node_modules/@fastscript/core-private/src/style-primitives.mjs +538 -0
  57. package/node_modules/@fastscript/core-private/src/style-system.mjs +461 -0
  58. package/node_modules/@fastscript/core-private/src/tenant.mjs +55 -0
  59. package/node_modules/@fastscript/core-private/src/typecheck.mjs +1464 -0
  60. package/node_modules/@fastscript/core-private/src/validate.mjs +22 -0
  61. package/node_modules/@fastscript/core-private/src/validation.mjs +88 -0
  62. package/node_modules/@fastscript/core-private/src/webhook.mjs +81 -0
  63. package/node_modules/@fastscript/core-private/src/worker.mjs +24 -0
  64. package/package.json +88 -8
  65. package/src/asset-optimizer.mjs +67 -0
  66. package/src/audit-log.mjs +50 -0
  67. package/src/auth.mjs +1 -115
  68. package/src/bench.mjs +20 -7
  69. package/src/build.mjs +1 -222
  70. package/src/cache.mjs +210 -20
  71. package/src/cli.mjs +29 -5
  72. package/src/compat.mjs +7 -1
  73. package/src/create.mjs +65 -11
  74. package/src/csp.mjs +26 -0
  75. package/src/db-cli.mjs +158 -18
  76. package/src/db-postgres-collection.mjs +110 -0
  77. package/src/deploy.mjs +1 -65
  78. package/src/docs-search.mjs +35 -0
  79. package/src/env.mjs +34 -5
  80. package/src/fs-diagnostics.mjs +70 -0
  81. package/src/fs-error-codes.mjs +126 -0
  82. package/src/fs-formatter.mjs +66 -0
  83. package/src/fs-linter.mjs +274 -0
  84. package/src/fs-normalize.mjs +17 -26
  85. package/src/fs-parser.mjs +1 -0
  86. package/src/generated/docs-search-index.mjs +3220 -0
  87. package/src/i18n.mjs +25 -0
  88. package/src/jobs.mjs +283 -32
  89. package/src/metrics.mjs +45 -0
  90. package/src/migration-wizard.mjs +16 -0
  91. package/src/module-loader.mjs +46 -0
  92. package/src/oauth-providers.mjs +103 -0
  93. package/src/plugins.mjs +194 -0
  94. package/src/retention.mjs +57 -0
  95. package/src/routes.mjs +178 -0
  96. package/src/scheduler.mjs +104 -0
  97. package/src/security.mjs +197 -19
  98. package/src/server-runtime.mjs +1 -339
  99. package/src/serverless-handler.mjs +20 -0
  100. package/src/session-policy.mjs +38 -0
  101. package/src/storage.mjs +1 -56
  102. package/src/style-system.mjs +461 -0
  103. package/src/tenant.mjs +55 -0
  104. package/src/typecheck.mjs +1 -0
  105. package/src/validate.mjs +5 -1
  106. package/src/validation.mjs +14 -5
  107. package/src/webhook.mjs +1 -71
  108. package/src/worker.mjs +23 -4
@@ -0,0 +1,538 @@
1
+ const BLOCK_CONFIG = {
2
+ Box: { tag: "div", base: ["fs-box"] },
3
+ Stack: { tag: "div", base: ["fs-box", "fs-stack"] },
4
+ Row: { tag: "div", base: ["fs-box", "fs-row"] },
5
+ Grid: { tag: "div", base: ["fs-box", "fs-grid"] },
6
+ Section: { tag: "section", base: ["fs-box", "fs-section"] },
7
+ Container: { tag: "div", base: ["fs-box", "fs-container"] },
8
+ Screen: { tag: "main", base: ["fs-box", "fs-screen"] },
9
+ Card: { tag: "article", base: ["fs-box", "fs-card"] },
10
+ Panel: { tag: "section", base: ["fs-box", "fs-panel"] },
11
+ Text: { tag: "p", base: ["fs-text"] },
12
+ Heading: { tag: "h2", base: ["fs-heading"] },
13
+ Code: { tag: "code", base: ["fs-code"] },
14
+ Label: { tag: "label", base: ["fs-label"] },
15
+ Badge: { tag: "span", base: ["fs-badge"] },
16
+ Link: { tag: "a", base: ["fs-link"] },
17
+ Button: { tag: "button", base: ["fs-button"] },
18
+ Textarea: { tag: "textarea", base: ["fs-input", "fs-textarea"] },
19
+ Select: { tag: "select", base: ["fs-input", "fs-select"] },
20
+ Field: { tag: "div", base: ["fs-field"] },
21
+ Alert: { tag: "div", base: ["fs-alert"] },
22
+ Empty: { tag: "div", base: ["fs-empty"] },
23
+ };
24
+
25
+ const SELF_CLOSING_CONFIG = {
26
+ Input: { tag: "input", base: ["fs-input"] },
27
+ Image: { tag: "img", base: ["fs-image"] },
28
+ Spacer: { tag: "div", base: ["fs-spacer"], defaults: { "aria-hidden": "true" } },
29
+ Loader: { tag: "div", base: ["fs-loader"], defaults: { role: "status", "aria-live": "polite" } },
30
+ Checkbox: { tag: "input", base: ["fs-checkbox"], defaults: { type: "checkbox" } },
31
+ Radio: { tag: "input", base: ["fs-radio"], defaults: { type: "radio" } },
32
+ Switch: { tag: "input", base: ["fs-switch"], defaults: { type: "checkbox", role: "switch" } },
33
+ };
34
+
35
+ const PRIMITIVE_NAMES = [...Object.keys(BLOCK_CONFIG), ...Object.keys(SELF_CLOSING_CONFIG)];
36
+ const PRIMITIVE_SET = new Set(PRIMITIVE_NAMES);
37
+ const SURFACE_VALUES = new Set(["plain", "subtle", "panel", "card", "elevated", "inverted"]);
38
+ const TONE_VALUES = new Set(["default", "muted", "primary", "secondary", "accent", "success", "warning", "error", "ghost", "inverse"]);
39
+ const SIZE_VALUES = new Set(["xs", "sm", "md", "lg", "xl", "2xl", "3xl", "4xl", "5xl"]);
40
+ const WEIGHT_VALUES = new Set(["thin", "light", "normal", "medium", "semibold", "bold", "extrabold", "black"]);
41
+ const SHADOW_VALUES = new Set(["none", "soft", "md", "lg", "glow"]);
42
+ const ALIGN_VALUES = new Set(["start", "center", "end", "stretch"]);
43
+ const JUSTIFY_VALUES = new Set(["start", "center", "end", "between", "around"]);
44
+ const ENTER_VALUES = new Set(["fade", "fade-up", "scale-in", "slide"]);
45
+ const HOVER_VALUES = new Set(["lift", "pulse"]);
46
+ const SEMANTIC_KEYS = new Set(["pad", "gap", "radius", "shadow", "surface", "tone", "size", "weight", "align", "justify", "cols", "enter", "hover", "level", "as"]);
47
+ const ATTR_RE = /([:@A-Za-z_][\w:.-]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|`([^`]*)`|([^\s"'`=<>]+)))?/g;
48
+ const INNER_PRIMITIVE_PATTERN = new RegExp(`<\\/?(?:${PRIMITIVE_NAMES.join("|")})\\b`);
49
+ const SELF_CLOSING_PATTERN = new RegExp(`<(${Object.keys(SELF_CLOSING_CONFIG).join("|")})\\b([^>]*)\\/?>`, "g");
50
+ const BLOCK_PATTERN = new RegExp(
51
+ `<(${Object.keys(BLOCK_CONFIG).join("|")})\\b([^>]*)>` +
52
+ `((?:(?!<\\/?(?:${PRIMITIVE_NAMES.join("|")})\\b)[\\s\\S])*)` +
53
+ `<\\/\\1>`,
54
+ "g",
55
+ );
56
+
57
+ function escapeAttr(value) {
58
+ return String(value)
59
+ .replace(/&/g, "&amp;")
60
+ .replace(/"/g, "&quot;");
61
+ }
62
+
63
+ function parseAttributes(raw = "") {
64
+ const attrs = [];
65
+ let match = null;
66
+ while ((match = ATTR_RE.exec(raw)) !== null) {
67
+ attrs.push({
68
+ name: match[1],
69
+ value: match[2] ?? match[3] ?? match[4] ?? match[5] ?? null,
70
+ });
71
+ }
72
+ return attrs;
73
+ }
74
+
75
+ function attrsToMap(attrs) {
76
+ const map = Object.create(null);
77
+ for (const attr of attrs) map[attr.name] = attr.value;
78
+ return map;
79
+ }
80
+
81
+ function headingTagFromAttrs(attrMap) {
82
+ const level = Number(attrMap.level || "");
83
+ if (Number.isInteger(level) && level >= 1 && level <= 6) return `h${level}`;
84
+ if (/^h[1-6]$/i.test(String(attrMap.as || ""))) return String(attrMap.as).toLowerCase();
85
+ const size = String(attrMap.size || "md");
86
+ if (size === "5xl" || size === "4xl") return "h1";
87
+ if (size === "3xl" || size === "2xl") return "h2";
88
+ if (size === "xl" || size === "lg") return "h3";
89
+ return "h4";
90
+ }
91
+
92
+ function resolveTag(name, attrMap, config) {
93
+ if (name === "Heading") return headingTagFromAttrs(attrMap);
94
+ if (name === "Button") {
95
+ if (attrMap.as) return String(attrMap.as).toLowerCase();
96
+ if (attrMap.href) return "a";
97
+ }
98
+ if (attrMap.as) return String(attrMap.as).toLowerCase();
99
+ return config.tag;
100
+ }
101
+
102
+ function buildClasses(name, attrMap, config) {
103
+ const classes = [...(config.base || [])];
104
+ if (attrMap.class) classes.push(...String(attrMap.class).split(/\s+/g).filter(Boolean));
105
+
106
+ if (attrMap.pad) classes.push(`fs-pad-${attrMap.pad}`);
107
+ if (attrMap.gap) classes.push(`fs-gap-${attrMap.gap}`);
108
+ if (attrMap.radius) classes.push(`fs-radius-${attrMap.radius}`);
109
+
110
+ const shadow = attrMap.shadow === "lg" ? "glow" : attrMap.shadow === "md" ? "soft" : attrMap.shadow;
111
+ if (shadow) classes.push(`fs-shadow-${shadow}`);
112
+ if (attrMap.surface) classes.push(`fs-surface-${attrMap.surface}`);
113
+
114
+ if (attrMap.align) classes.push(`fs-align-${attrMap.align}`);
115
+ if (attrMap.justify) classes.push(`fs-justify-${attrMap.justify}`);
116
+ if (attrMap.cols) classes.push(`fs-grid-cols-${attrMap.cols}`);
117
+ if (attrMap.enter) classes.push(`fs-enter-${attrMap.enter}`);
118
+ if (attrMap.hover) classes.push(`fs-hover-${attrMap.hover}`);
119
+
120
+ if (name === "Heading") {
121
+ classes.push(`fs-heading-size-${attrMap.size || "md"}`);
122
+ if (attrMap.tone) classes.push(`fs-tone-${attrMap.tone}`);
123
+ if (attrMap.weight) classes.push(`fs-weight-${attrMap.weight}`);
124
+ } else if (name === "Text" || name === "Code" || name === "Label" || name === "Badge" || name === "Link") {
125
+ if (attrMap.size) classes.push(`fs-text-size-${attrMap.size}`);
126
+ if (attrMap.tone) classes.push(`fs-tone-${attrMap.tone}`);
127
+ if (attrMap.weight) classes.push(`fs-weight-${attrMap.weight}`);
128
+ } else if (name === "Button") {
129
+ classes.push(`fs-button-tone-${attrMap.tone || "primary"}`);
130
+ classes.push(`fs-button-size-${attrMap.size || "md"}`);
131
+ } else {
132
+ if (attrMap.tone) classes.push(`fs-tone-${attrMap.tone}`);
133
+ if (attrMap.size) classes.push(`fs-text-size-${attrMap.size}`);
134
+ if (attrMap.weight) classes.push(`fs-weight-${attrMap.weight}`);
135
+ }
136
+
137
+ return [...new Set(classes.filter(Boolean))];
138
+ }
139
+
140
+ function passThroughAttribute(attr) {
141
+ if (!attr || !attr.name) return false;
142
+ if (SEMANTIC_KEYS.has(attr.name)) return false;
143
+ if (attr.name === "class") return false;
144
+ return true;
145
+ }
146
+
147
+ function serializeOpenTag(tagName, attrs, classes) {
148
+ const parts = [`<${tagName}`];
149
+ if (classes.length) parts.push(` class="${escapeAttr(classes.join(" "))}"`);
150
+ for (const attr of attrs) {
151
+ if (!passThroughAttribute(attr)) continue;
152
+ if (attr.value === null) parts.push(` ${attr.name}`);
153
+ else parts.push(` ${attr.name}="${escapeAttr(attr.value)}"`);
154
+ }
155
+ parts.push(">");
156
+ return parts.join("");
157
+ }
158
+
159
+ function serializeSelfClosingTag(tagName, attrs, classes) {
160
+ const parts = [`<${tagName}`];
161
+ if (classes.length) parts.push(` class="${escapeAttr(classes.join(" "))}"`);
162
+ for (const attr of attrs) {
163
+ if (!passThroughAttribute(attr)) continue;
164
+ if (attr.value === null) parts.push(` ${attr.name}`);
165
+ else parts.push(` ${attr.name}="${escapeAttr(attr.value)}"`);
166
+ }
167
+ parts.push(" />");
168
+ return parts.join("");
169
+ }
170
+
171
+ function normalizeAttributes(name, attrs, config) {
172
+ const out = [...attrs];
173
+ const map = attrsToMap(out);
174
+ for (const [key, value] of Object.entries(config.defaults || {})) {
175
+ if (!(key in map)) out.push({ name: key, value });
176
+ }
177
+ if (name === "Button") {
178
+ const tag = resolveTag(name, attrsToMap(out), config);
179
+ const hasType = out.some((attr) => attr.name === "type");
180
+ if (tag === "button" && !hasType) out.push({ name: "type", value: "button" });
181
+ }
182
+ if (name === "Heading") {
183
+ out.push({ name: "data-fs-heading", value: "true" });
184
+ }
185
+ return out;
186
+ }
187
+
188
+ function renderPrimitiveNode(name, rawAttrs, innerHtml = "") {
189
+ const config = BLOCK_CONFIG[name] || SELF_CLOSING_CONFIG[name];
190
+ if (!config) return "";
191
+ const attrs = normalizeAttributes(name, parseAttributes(rawAttrs), config);
192
+ const attrMap = attrsToMap(attrs);
193
+ const tag = resolveTag(name, attrMap, config);
194
+ const classes = buildClasses(name, attrMap, config);
195
+ if (SELF_CLOSING_CONFIG[name]) {
196
+ return serializeSelfClosingTag(tag, attrs, classes);
197
+ }
198
+ return `${serializeOpenTag(tag, attrs, classes)}${innerHtml}</${tag}>`;
199
+ }
200
+
201
+ export function transformStylePrimitives(html = "") {
202
+ let out = String(html || "");
203
+ if (!INNER_PRIMITIVE_PATTERN.test(out)) return out;
204
+
205
+ let guard = 0;
206
+ while (guard < 200) {
207
+ let changed = false;
208
+
209
+ out = out.replace(SELF_CLOSING_PATTERN, (full, name, attrs) => {
210
+ changed = true;
211
+ return renderPrimitiveNode(name, attrs || "", "");
212
+ });
213
+
214
+ out = out.replace(BLOCK_PATTERN, (full, name, attrs, inner) => {
215
+ changed = true;
216
+ return renderPrimitiveNode(name, attrs || "", inner || "");
217
+ });
218
+
219
+ if (!changed) break;
220
+ guard += 1;
221
+ }
222
+
223
+ return out;
224
+ }
225
+
226
+ export function buildStylePrimitiveRuntimeSource() {
227
+ return `
228
+ const FS_BLOCK_CONFIG = ${JSON.stringify(BLOCK_CONFIG)};
229
+ const FS_SELF_CLOSING_CONFIG = ${JSON.stringify(SELF_CLOSING_CONFIG)};
230
+ const FS_SEMANTIC_KEYS = new Set(${JSON.stringify([...SEMANTIC_KEYS])});
231
+
232
+ function fsAttrMap(el) {
233
+ const out = Object.create(null);
234
+ for (const attr of Array.from(el.attributes || [])) out[attr.name] = attr.value;
235
+ return out;
236
+ }
237
+
238
+ function fsHeadingTag(attrMap) {
239
+ const level = Number(attrMap.level || "");
240
+ if (Number.isInteger(level) && level >= 1 && level <= 6) return "h" + level;
241
+ if (/^h[1-6]$/i.test(String(attrMap.as || ""))) return String(attrMap.as).toLowerCase();
242
+ const size = String(attrMap.size || "md");
243
+ if (size === "5xl" || size === "4xl") return "h1";
244
+ if (size === "3xl" || size === "2xl") return "h2";
245
+ if (size === "xl" || size === "lg") return "h3";
246
+ return "h4";
247
+ }
248
+
249
+ function fsResolveTag(name, attrMap, config) {
250
+ if (name === "Heading") return fsHeadingTag(attrMap);
251
+ if (name === "Button") {
252
+ if (attrMap.as) return String(attrMap.as).toLowerCase();
253
+ if (attrMap.href) return "a";
254
+ }
255
+ if (attrMap.as) return String(attrMap.as).toLowerCase();
256
+ return config.tag;
257
+ }
258
+
259
+ function fsClasses(name, attrMap, config) {
260
+ const classes = [...(config.base || [])];
261
+ if (attrMap.class) classes.push(...String(attrMap.class).split(/\\s+/g).filter(Boolean));
262
+ if (attrMap.pad) classes.push("fs-pad-" + attrMap.pad);
263
+ if (attrMap.gap) classes.push("fs-gap-" + attrMap.gap);
264
+ if (attrMap.radius) classes.push("fs-radius-" + attrMap.radius);
265
+ const shadow = attrMap.shadow === "lg" ? "glow" : attrMap.shadow === "md" ? "soft" : attrMap.shadow;
266
+ if (shadow) classes.push("fs-shadow-" + shadow);
267
+ if (attrMap.surface) classes.push("fs-surface-" + attrMap.surface);
268
+ if (attrMap.align) classes.push("fs-align-" + attrMap.align);
269
+ if (attrMap.justify) classes.push("fs-justify-" + attrMap.justify);
270
+ if (attrMap.cols) classes.push("fs-grid-cols-" + attrMap.cols);
271
+ if (attrMap.enter) classes.push("fs-enter-" + attrMap.enter);
272
+ if (attrMap.hover) classes.push("fs-hover-" + attrMap.hover);
273
+ if (name === "Heading") {
274
+ classes.push("fs-heading-size-" + (attrMap.size || "md"));
275
+ if (attrMap.tone) classes.push("fs-tone-" + attrMap.tone);
276
+ if (attrMap.weight) classes.push("fs-weight-" + attrMap.weight);
277
+ } else if (name === "Text" || name === "Code" || name === "Label" || name === "Badge" || name === "Link") {
278
+ if (attrMap.size) classes.push("fs-text-size-" + attrMap.size);
279
+ if (attrMap.tone) classes.push("fs-tone-" + attrMap.tone);
280
+ if (attrMap.weight) classes.push("fs-weight-" + attrMap.weight);
281
+ } else if (name === "Button") {
282
+ classes.push("fs-button-tone-" + (attrMap.tone || "primary"));
283
+ classes.push("fs-button-size-" + (attrMap.size || "md"));
284
+ } else {
285
+ if (attrMap.tone) classes.push("fs-tone-" + attrMap.tone);
286
+ if (attrMap.size) classes.push("fs-text-size-" + attrMap.size);
287
+ if (attrMap.weight) classes.push("fs-weight-" + attrMap.weight);
288
+ }
289
+ return [...new Set(classes.filter(Boolean))];
290
+ }
291
+
292
+ function upgradePrimitiveElement(el) {
293
+ const name = el.tagName && (el.tagName[0] + el.tagName.slice(1).toLowerCase());
294
+ const config = FS_BLOCK_CONFIG[name] || FS_SELF_CLOSING_CONFIG[name];
295
+ if (!config) return el;
296
+ const attrMap = fsAttrMap(el);
297
+ const tag = fsResolveTag(name, attrMap, config);
298
+ const next = document.createElement(tag);
299
+ const classes = fsClasses(name, attrMap, config);
300
+ if (classes.length) next.className = classes.join(" ");
301
+ for (const [key, value] of Object.entries(config.defaults || {})) {
302
+ if (!el.hasAttribute(key)) next.setAttribute(key, value);
303
+ }
304
+ for (const attr of Array.from(el.attributes || [])) {
305
+ if (attr.name === "class" || FS_SEMANTIC_KEYS.has(attr.name)) continue;
306
+ next.setAttribute(attr.name, attr.value);
307
+ }
308
+ if (name === "Button" && tag === "button" && !next.hasAttribute("type")) {
309
+ next.setAttribute("type", "button");
310
+ }
311
+ if (name === "Heading") next.setAttribute("data-fs-heading", "true");
312
+ while (el.firstChild) next.appendChild(el.firstChild);
313
+ el.replaceWith(next);
314
+ return next;
315
+ }
316
+
317
+ function primitiveSelector() {
318
+ return Object.keys(FS_BLOCK_CONFIG).concat(Object.keys(FS_SELF_CLOSING_CONFIG)).map((name) => name.toLowerCase()).join(",");
319
+ }
320
+
321
+ export function upgradeStylePrimitivesInDom(root) {
322
+ if (!root || typeof root.querySelectorAll !== "function") return;
323
+ let guard = 0;
324
+ while (guard < 25) {
325
+ const nodes = Array.from(root.querySelectorAll(primitiveSelector()));
326
+ if (!nodes.length) break;
327
+ for (const node of nodes) {
328
+ if (node && node.isConnected) upgradePrimitiveElement(node);
329
+ }
330
+ guard += 1;
331
+ }
332
+ }
333
+ `;
334
+ }
335
+
336
+ export function buildStylePrimitiveServerSource() {
337
+ return `
338
+ const FS_BLOCK_CONFIG = ${JSON.stringify(BLOCK_CONFIG)};
339
+ const FS_SELF_CLOSING_CONFIG = ${JSON.stringify(SELF_CLOSING_CONFIG)};
340
+ const FS_SEMANTIC_KEYS = new Set(${JSON.stringify([...SEMANTIC_KEYS])});
341
+ const FS_PRIMITIVE_NAMES = ${JSON.stringify(PRIMITIVE_NAMES)};
342
+ const FS_ATTR_RE = /([:@A-Za-z_][\\w:.-]*)(?:\\s*=\\s*(?:\\"([^\\"]*)\\"|'([^']*)'|\`([^\`]*)\`|([^\\s\\"'\`=<>]+)))?/g;
343
+ const FS_INNER_PRIMITIVE_PATTERN = new RegExp("<\\\\/?(?:" + FS_PRIMITIVE_NAMES.join("|") + ")\\\\b");
344
+ const FS_SELF_CLOSING_PATTERN = new RegExp("<(" + Object.keys(FS_SELF_CLOSING_CONFIG).join("|") + ")\\\\b([^>]*)\\\\/?>", "g");
345
+ const FS_BLOCK_PATTERN = new RegExp(
346
+ "<(" + Object.keys(FS_BLOCK_CONFIG).join("|") + ")\\\\b([^>]*)>" +
347
+ "((?:(?!<\\\\/?(?:" + FS_PRIMITIVE_NAMES.join("|") + ")\\\\b)[\\\\s\\\\S])*)" +
348
+ "<\\\\/\\\\1>",
349
+ "g",
350
+ );
351
+
352
+ function fsEscapeAttr(value) {
353
+ return String(value).replace(/&/g, "&amp;").replace(/\\"/g, "&quot;");
354
+ }
355
+
356
+ function fsParseAttributes(raw = "") {
357
+ const attrs = [];
358
+ let match = null;
359
+ FS_ATTR_RE.lastIndex = 0;
360
+ while ((match = FS_ATTR_RE.exec(raw)) !== null) {
361
+ attrs.push({ name: match[1], value: match[2] ?? match[3] ?? match[4] ?? match[5] ?? null });
362
+ }
363
+ return attrs;
364
+ }
365
+
366
+ function fsAttrsToMap(attrs) {
367
+ const map = Object.create(null);
368
+ for (const attr of attrs) map[attr.name] = attr.value;
369
+ return map;
370
+ }
371
+
372
+ function fsHeadingTag(attrMap) {
373
+ const level = Number(attrMap.level || "");
374
+ if (Number.isInteger(level) && level >= 1 && level <= 6) return "h" + level;
375
+ if (/^h[1-6]$/i.test(String(attrMap.as || ""))) return String(attrMap.as).toLowerCase();
376
+ const size = String(attrMap.size || "md");
377
+ if (size === "5xl" || size === "4xl") return "h1";
378
+ if (size === "3xl" || size === "2xl") return "h2";
379
+ if (size === "xl" || size === "lg") return "h3";
380
+ return "h4";
381
+ }
382
+
383
+ function fsResolveTag(name, attrMap, config) {
384
+ if (name === "Heading") return fsHeadingTag(attrMap);
385
+ if (name === "Button") {
386
+ if (attrMap.as) return String(attrMap.as).toLowerCase();
387
+ if (attrMap.href) return "a";
388
+ }
389
+ if (attrMap.as) return String(attrMap.as).toLowerCase();
390
+ return config.tag;
391
+ }
392
+
393
+ function fsClasses(name, attrMap, config) {
394
+ const classes = [...(config.base || [])];
395
+ if (attrMap.class) classes.push(...String(attrMap.class).split(/\\s+/g).filter(Boolean));
396
+ if (attrMap.pad) classes.push("fs-pad-" + attrMap.pad);
397
+ if (attrMap.gap) classes.push("fs-gap-" + attrMap.gap);
398
+ if (attrMap.radius) classes.push("fs-radius-" + attrMap.radius);
399
+ const shadow = attrMap.shadow === "lg" ? "glow" : attrMap.shadow === "md" ? "soft" : attrMap.shadow;
400
+ if (shadow) classes.push("fs-shadow-" + shadow);
401
+ if (attrMap.surface) classes.push("fs-surface-" + attrMap.surface);
402
+ if (attrMap.align) classes.push("fs-align-" + attrMap.align);
403
+ if (attrMap.justify) classes.push("fs-justify-" + attrMap.justify);
404
+ if (attrMap.cols) classes.push("fs-grid-cols-" + attrMap.cols);
405
+ if (attrMap.enter) classes.push("fs-enter-" + attrMap.enter);
406
+ if (attrMap.hover) classes.push("fs-hover-" + attrMap.hover);
407
+ if (name === "Heading") {
408
+ classes.push("fs-heading-size-" + (attrMap.size || "md"));
409
+ if (attrMap.tone) classes.push("fs-tone-" + attrMap.tone);
410
+ if (attrMap.weight) classes.push("fs-weight-" + attrMap.weight);
411
+ } else if (name === "Text" || name === "Code" || name === "Label" || name === "Badge" || name === "Link") {
412
+ if (attrMap.size) classes.push("fs-text-size-" + attrMap.size);
413
+ if (attrMap.tone) classes.push("fs-tone-" + attrMap.tone);
414
+ if (attrMap.weight) classes.push("fs-weight-" + attrMap.weight);
415
+ } else if (name === "Button") {
416
+ classes.push("fs-button-tone-" + (attrMap.tone || "primary"));
417
+ classes.push("fs-button-size-" + (attrMap.size || "md"));
418
+ } else {
419
+ if (attrMap.tone) classes.push("fs-tone-" + attrMap.tone);
420
+ if (attrMap.size) classes.push("fs-text-size-" + attrMap.size);
421
+ if (attrMap.weight) classes.push("fs-weight-" + attrMap.weight);
422
+ }
423
+ return [...new Set(classes.filter(Boolean))];
424
+ }
425
+
426
+ function fsPassThroughAttribute(attr) {
427
+ if (!attr || !attr.name) return false;
428
+ if (FS_SEMANTIC_KEYS.has(attr.name)) return false;
429
+ if (attr.name === "class") return false;
430
+ return true;
431
+ }
432
+
433
+ function fsNormalizeAttributes(name, attrs, config) {
434
+ const out = [...attrs];
435
+ const map = fsAttrsToMap(out);
436
+ for (const [key, value] of Object.entries(config.defaults || {})) {
437
+ if (!(key in map)) out.push({ name: key, value });
438
+ }
439
+ if (name === "Button") {
440
+ const tag = fsResolveTag(name, fsAttrsToMap(out), config);
441
+ const hasType = out.some((attr) => attr.name === "type");
442
+ if (tag === "button" && !hasType) out.push({ name: "type", value: "button" });
443
+ }
444
+ if (name === "Heading") out.push({ name: "data-fs-heading", value: "true" });
445
+ return out;
446
+ }
447
+
448
+ function fsSerializeOpenTag(tagName, attrs, classes) {
449
+ const parts = ["<" + tagName];
450
+ if (classes.length) parts.push(" class=\\"" + fsEscapeAttr(classes.join(" ")) + "\\"");
451
+ for (const attr of attrs) {
452
+ if (!fsPassThroughAttribute(attr)) continue;
453
+ if (attr.value === null) parts.push(" " + attr.name);
454
+ else parts.push(" " + attr.name + "=\\"" + fsEscapeAttr(attr.value) + "\\"");
455
+ }
456
+ parts.push(">");
457
+ return parts.join("");
458
+ }
459
+
460
+ function fsSerializeSelfClosingTag(tagName, attrs, classes) {
461
+ const parts = ["<" + tagName];
462
+ if (classes.length) parts.push(" class=\\"" + fsEscapeAttr(classes.join(" ")) + "\\"");
463
+ for (const attr of attrs) {
464
+ if (!fsPassThroughAttribute(attr)) continue;
465
+ if (attr.value === null) parts.push(" " + attr.name);
466
+ else parts.push(" " + attr.name + "=\\"" + fsEscapeAttr(attr.value) + "\\"");
467
+ }
468
+ parts.push(" />");
469
+ return parts.join("");
470
+ }
471
+
472
+ function fsRenderPrimitiveNode(name, rawAttrs, innerHtml = "") {
473
+ const config = FS_BLOCK_CONFIG[name] || FS_SELF_CLOSING_CONFIG[name];
474
+ if (!config) return "";
475
+ const attrs = fsNormalizeAttributes(name, fsParseAttributes(rawAttrs), config);
476
+ const attrMap = fsAttrsToMap(attrs);
477
+ const tag = fsResolveTag(name, attrMap, config);
478
+ const classes = fsClasses(name, attrMap, config);
479
+ if (FS_SELF_CLOSING_CONFIG[name]) return fsSerializeSelfClosingTag(tag, attrs, classes);
480
+ return fsSerializeOpenTag(tag, attrs, classes) + innerHtml + "</" + tag + ">";
481
+ }
482
+
483
+ function transformStylePrimitives(html = "") {
484
+ let out = String(html || "");
485
+ if (!FS_INNER_PRIMITIVE_PATTERN.test(out)) return out;
486
+ let guard = 0;
487
+ while (guard < 200) {
488
+ let changed = false;
489
+ out = out.replace(FS_SELF_CLOSING_PATTERN, (full, name, attrs) => {
490
+ changed = true;
491
+ return fsRenderPrimitiveNode(name, attrs || "", "");
492
+ });
493
+ out = out.replace(FS_BLOCK_PATTERN, (full, name, attrs, inner) => {
494
+ changed = true;
495
+ return fsRenderPrimitiveNode(name, attrs || "", inner || "");
496
+ });
497
+ if (!changed) break;
498
+ guard += 1;
499
+ }
500
+ return out;
501
+ }
502
+ `;
503
+ }
504
+
505
+ export function validatePrimitiveSemanticValue(name, value, tokens = {}) {
506
+ const raw = String(value || "").trim();
507
+ if (name === "pad" || name === "gap") return Boolean(tokens.space && Object.prototype.hasOwnProperty.call(tokens.space, raw));
508
+ if (name === "radius") return Boolean(tokens.radius && Object.prototype.hasOwnProperty.call(tokens.radius, raw));
509
+ if (name === "shadow") return SHADOW_VALUES.has(raw);
510
+ if (name === "surface") return SURFACE_VALUES.has(raw);
511
+ if (name === "tone") return TONE_VALUES.has(raw);
512
+ if (name === "size") return SIZE_VALUES.has(raw);
513
+ if (name === "weight") return WEIGHT_VALUES.has(raw);
514
+ if (name === "align") return ALIGN_VALUES.has(raw);
515
+ if (name === "justify") return JUSTIFY_VALUES.has(raw);
516
+ if (name === "enter") return ENTER_VALUES.has(raw);
517
+ if (name === "hover") return HOVER_VALUES.has(raw);
518
+ if (name === "cols") return /^\d+$/.test(raw) && Number(raw) >= 1 && Number(raw) <= 12;
519
+ if (name === "level") return /^[1-6]$/.test(raw);
520
+ if (name === "as") return true;
521
+ return true;
522
+ }
523
+
524
+ export function validatePrimitiveMarkup(source = "", file = "", tokens = {}, errors = []) {
525
+ const TAG_RE = new RegExp(`<(${PRIMITIVE_NAMES.join("|")})\\b([^>]*)\\/?>`, "g");
526
+ let match = null;
527
+ while ((match = TAG_RE.exec(String(source || ""))) !== null) {
528
+ const tagName = match[1];
529
+ const attrs = parseAttributes(match[2] || "");
530
+ for (const attr of attrs) {
531
+ if (!SEMANTIC_KEYS.has(attr.name) || attr.value === null) continue;
532
+ if (!validatePrimitiveSemanticValue(attr.name, attr.value, tokens)) {
533
+ errors.push(`${file}: primitive <${tagName}> has invalid ${attr.name}="${attr.value}"`);
534
+ }
535
+ }
536
+ }
537
+ return errors;
538
+ }