stoop 0.3.0 → 0.4.1

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 (65) hide show
  1. package/README.md +125 -0
  2. package/dist/api/core-api.d.ts +34 -0
  3. package/dist/api/styled.d.ts +0 -1
  4. package/dist/api/theme-provider.d.ts +41 -0
  5. package/dist/constants.d.ts +11 -1
  6. package/dist/core/compiler.d.ts +11 -0
  7. package/dist/core/theme-manager.d.ts +34 -3
  8. package/dist/create-stoop-internal.d.ts +30 -0
  9. package/dist/create-stoop-ssr.d.ts +10 -0
  10. package/dist/create-stoop-ssr.js +16 -0
  11. package/dist/create-stoop.d.ts +32 -1
  12. package/dist/create-stoop.js +16 -147
  13. package/dist/inject.d.ts +113 -0
  14. package/dist/types/index.d.ts +147 -12
  15. package/dist/types/react-polymorphic-types.d.ts +13 -2
  16. package/dist/utils/auto-preload.d.ts +45 -0
  17. package/dist/utils/helpers.d.ts +64 -0
  18. package/dist/utils/storage.d.ts +148 -0
  19. package/dist/utils/{string.d.ts → theme-utils.d.ts} +20 -3
  20. package/dist/utils/theme.d.ts +14 -2
  21. package/package.json +44 -17
  22. package/dist/api/create-theme.d.ts +0 -13
  23. package/dist/api/create-theme.js +0 -43
  24. package/dist/api/css.d.ts +0 -16
  25. package/dist/api/css.js +0 -20
  26. package/dist/api/global-css.js +0 -89
  27. package/dist/api/keyframes.d.ts +0 -16
  28. package/dist/api/keyframes.js +0 -95
  29. package/dist/api/provider.d.ts +0 -19
  30. package/dist/api/provider.js +0 -109
  31. package/dist/api/styled.js +0 -170
  32. package/dist/api/use-theme.d.ts +0 -13
  33. package/dist/api/use-theme.js +0 -21
  34. package/dist/constants.js +0 -144
  35. package/dist/core/cache.js +0 -68
  36. package/dist/core/compiler.js +0 -198
  37. package/dist/core/theme-manager.js +0 -97
  38. package/dist/core/variants.js +0 -32
  39. package/dist/create-stoop-server.d.ts +0 -33
  40. package/dist/create-stoop-server.js +0 -130
  41. package/dist/index.d.ts +0 -6
  42. package/dist/index.js +0 -5
  43. package/dist/inject/browser.d.ts +0 -58
  44. package/dist/inject/browser.js +0 -149
  45. package/dist/inject/dedup.d.ts +0 -29
  46. package/dist/inject/dedup.js +0 -38
  47. package/dist/inject/index.d.ts +0 -40
  48. package/dist/inject/index.js +0 -75
  49. package/dist/inject/ssr.d.ts +0 -27
  50. package/dist/inject/ssr.js +0 -46
  51. package/dist/ssr.d.ts +0 -21
  52. package/dist/ssr.js +0 -19
  53. package/dist/types/index.js +0 -5
  54. package/dist/utils/environment.d.ts +0 -6
  55. package/dist/utils/environment.js +0 -12
  56. package/dist/utils/string.js +0 -253
  57. package/dist/utils/theme-map.d.ts +0 -22
  58. package/dist/utils/theme-map.js +0 -97
  59. package/dist/utils/theme-validation.d.ts +0 -13
  60. package/dist/utils/theme-validation.js +0 -36
  61. package/dist/utils/theme.js +0 -279
  62. package/dist/utils/type-guards.d.ts +0 -26
  63. package/dist/utils/type-guards.js +0 -38
  64. package/dist/utils/utilities.d.ts +0 -14
  65. package/dist/utils/utilities.js +0 -43
@@ -1,253 +0,0 @@
1
- /**
2
- * String utility functions.
3
- * Provides hashing for class name generation, camelCase to kebab-case conversion,
4
- * and CSS sanitization utilities for security.
5
- */
6
- import { LRUCache } from "../core/cache";
7
- const sanitizeCacheSizeLimit = 1000;
8
- let cachedRootRegex = null;
9
- const selectorCache = new LRUCache(sanitizeCacheSizeLimit);
10
- const propertyNameCache = new LRUCache(sanitizeCacheSizeLimit);
11
- const sanitizeClassNameCache = new LRUCache(sanitizeCacheSizeLimit);
12
- const variableNameCache = new LRUCache(sanitizeCacheSizeLimit);
13
- /**
14
- * Generates a hash string from an input string using FNV-1a algorithm.
15
- * Includes string length to reduce collision probability.
16
- *
17
- * @param str - String to hash
18
- * @returns Hashed string
19
- */
20
- export function hash(str) {
21
- const FNV_OFFSET_BASIS = 2166136261;
22
- const FNV_PRIME = 16777619;
23
- let hash = FNV_OFFSET_BASIS;
24
- for (let i = 0; i < str.length; i++) {
25
- hash ^= str.charCodeAt(i);
26
- hash = Math.imul(hash, FNV_PRIME);
27
- }
28
- hash ^= str.length;
29
- return (hash >>> 0).toString(36);
30
- }
31
- /**
32
- * Generates a hash string from an object by stringifying it.
33
- *
34
- * @param obj - Object to hash
35
- * @returns Hashed string
36
- */
37
- export function hashObject(obj) {
38
- try {
39
- return hash(JSON.stringify(obj));
40
- }
41
- catch {
42
- return hash(String(obj));
43
- }
44
- }
45
- /**
46
- * Converts a camelCase string to kebab-case.
47
- *
48
- * @param str - String to convert
49
- * @returns Kebab-case string
50
- */
51
- export function toKebabCase(str) {
52
- return str.replace(/([A-Z])/g, "-$1").toLowerCase();
53
- }
54
- /**
55
- * Internal function to escape CSS values with optional brace escaping.
56
- *
57
- * @param value - Value to escape
58
- * @param escapeBraces - Whether to escape curly braces
59
- * @returns Escaped value string
60
- */
61
- function escapeCSSValueInternal(value, escapeBraces = false) {
62
- const str = String(value);
63
- let result = str
64
- .replace(/\\/g, "\\\\")
65
- .replace(/"/g, '\\"')
66
- .replace(/'/g, "\\'")
67
- .replace(/;/g, "\\;")
68
- .replace(/\n/g, "\\A ")
69
- .replace(/\r/g, "")
70
- .replace(/\f/g, "\\C ");
71
- if (escapeBraces) {
72
- result = result.replace(/\{/g, "\\7B ").replace(/\}/g, "\\7D ");
73
- }
74
- return result;
75
- }
76
- /**
77
- * Escapes CSS property values to prevent injection attacks.
78
- * Escapes quotes, semicolons, and other special characters.
79
- *
80
- * @param value - Value to escape
81
- * @returns Escaped value string
82
- */
83
- export function escapeCSSValue(value) {
84
- return escapeCSSValueInternal(value, false);
85
- }
86
- /**
87
- * Validates and sanitizes CSS selectors to prevent injection attacks.
88
- * Only allows safe selector characters. Returns empty string for invalid selectors.
89
- * Uses memoization for performance.
90
- *
91
- * @param selector - Selector to sanitize
92
- * @returns Sanitized selector string or empty string if invalid
93
- */
94
- export function sanitizeCSSSelector(selector) {
95
- const cached = selectorCache.get(selector);
96
- if (cached !== undefined) {
97
- return cached;
98
- }
99
- const sanitized = selector.replace(/[^a-zA-Z0-9\s\-_>+~:.#[\]&@()]/g, "");
100
- const result = !sanitized.trim() || /^[>+~:.#[\]&@()\s]+$/.test(sanitized) ? "" : sanitized;
101
- selectorCache.set(selector, result);
102
- return result;
103
- }
104
- /**
105
- * Validates and sanitizes CSS variable names to prevent injection attacks.
106
- * CSS custom properties must start with -- and contain only valid characters.
107
- * Uses memoization for performance.
108
- *
109
- * @param name - Variable name to sanitize
110
- * @returns Sanitized variable name
111
- */
112
- export function sanitizeCSSVariableName(name) {
113
- const cached = variableNameCache.get(name);
114
- if (cached !== undefined) {
115
- return cached;
116
- }
117
- const sanitized = name.replace(/[^a-zA-Z0-9-_]/g, "-");
118
- const cleaned = sanitized.replace(/^[\d-]+/, "").replace(/^-+/, "");
119
- const result = cleaned || "invalid";
120
- variableNameCache.set(name, result);
121
- return result;
122
- }
123
- /**
124
- * Escapes CSS variable values to prevent injection attacks.
125
- *
126
- * @param value - Value to escape
127
- * @returns Escaped value string
128
- */
129
- export function escapeCSSVariableValue(value) {
130
- return escapeCSSValueInternal(value, true);
131
- }
132
- /**
133
- * Sanitizes prefix for use in CSS selectors and class names.
134
- * Only allows alphanumeric characters, hyphens, and underscores.
135
- * Defaults to "stoop" if prefix is empty or becomes empty after sanitization.
136
- *
137
- * @param prefix - Prefix to sanitize
138
- * @returns Sanitized prefix string (never empty, defaults to "stoop")
139
- */
140
- export function sanitizePrefix(prefix) {
141
- if (!prefix) {
142
- return "stoop";
143
- }
144
- const sanitized = prefix.replace(/[^a-zA-Z0-9-_]/g, "");
145
- const cleaned = sanitized.replace(/^[\d-]+/, "").replace(/^-+/, "");
146
- // Return "stoop" as default if sanitization results in empty string
147
- return cleaned || "stoop";
148
- }
149
- /**
150
- * Sanitizes media query strings to prevent injection attacks.
151
- * Only allows safe characters for media queries.
152
- *
153
- * @param mediaQuery - Media query string to sanitize
154
- * @returns Sanitized media query string or empty string if invalid
155
- */
156
- export function sanitizeMediaQuery(mediaQuery) {
157
- if (!mediaQuery || typeof mediaQuery !== "string") {
158
- return "";
159
- }
160
- const sanitized = mediaQuery.replace(/[^a-zA-Z0-9\s():,<>=\-@]/g, "");
161
- if (!sanitized.trim() || !/[a-zA-Z]/.test(sanitized)) {
162
- return "";
163
- }
164
- return sanitized;
165
- }
166
- /**
167
- * Sanitizes CSS class names to prevent injection attacks.
168
- * Only allows valid CSS class name characters.
169
- * Uses memoization for performance.
170
- *
171
- * @param className - Class name(s) to sanitize
172
- * @returns Sanitized class name string
173
- */
174
- export function sanitizeClassName(className) {
175
- if (!className || typeof className !== "string") {
176
- return "";
177
- }
178
- const cached = sanitizeClassNameCache.get(className);
179
- if (cached !== undefined) {
180
- return cached;
181
- }
182
- const classes = className.trim().split(/\s+/);
183
- const sanitizedClasses = [];
184
- for (const cls of classes) {
185
- if (!cls) {
186
- continue;
187
- }
188
- const sanitized = cls.replace(/[^a-zA-Z0-9-_]/g, "");
189
- const cleaned = sanitized.replace(/^\d+/, "");
190
- if (cleaned && /^[a-zA-Z-_]/.test(cleaned)) {
191
- sanitizedClasses.push(cleaned);
192
- }
193
- }
194
- const result = sanitizedClasses.join(" ");
195
- sanitizeClassNameCache.set(className, result);
196
- return result;
197
- }
198
- /**
199
- * Sanitizes CSS property names to prevent injection attacks.
200
- * Uses memoization for performance.
201
- *
202
- * @param propertyName - Property name to sanitize
203
- * @returns Sanitized property name
204
- */
205
- export function sanitizeCSSPropertyName(propertyName) {
206
- if (!propertyName || typeof propertyName !== "string") {
207
- return "";
208
- }
209
- const cached = propertyNameCache.get(propertyName);
210
- if (cached !== undefined) {
211
- return cached;
212
- }
213
- const kebab = toKebabCase(propertyName);
214
- const sanitized = kebab.replace(/[^a-zA-Z0-9-]/g, "").replace(/^-+|-+$/g, "");
215
- const result = sanitized.replace(/^\d+/, "") || "";
216
- propertyNameCache.set(propertyName, result);
217
- return result;
218
- }
219
- /**
220
- * Validates keyframe percentage keys (e.g., "0%", "50%", "from", "to").
221
- *
222
- * @param key - Keyframe key to validate
223
- * @returns True if key is valid
224
- */
225
- export function validateKeyframeKey(key) {
226
- if (!key || typeof key !== "string") {
227
- return false;
228
- }
229
- if (key === "from" || key === "to") {
230
- return true;
231
- }
232
- const percentageMatch = /^\d+(\.\d+)?%$/.test(key);
233
- if (percentageMatch) {
234
- const num = parseFloat(key);
235
- return num >= 0 && num <= 100;
236
- }
237
- return false;
238
- }
239
- /**
240
- * Gets a pre-compiled regex for matching :root CSS selector blocks.
241
- * Uses caching for performance.
242
- *
243
- * @param prefix - Optional prefix (unused, kept for API compatibility)
244
- * @returns RegExp for matching :root selector blocks
245
- */
246
- export function getRootRegex(prefix = "") {
247
- if (!cachedRootRegex) {
248
- const rootSelector = ":root";
249
- const escapedSelector = rootSelector.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
250
- cachedRootRegex = new RegExp(`${escapedSelector}\\s*\\{[\\s\\S]*?\\}`, "g");
251
- }
252
- return cachedRootRegex;
253
- }
@@ -1,22 +0,0 @@
1
- /**
2
- * ThemeMap utilities for property-aware token resolution.
3
- * Maps CSS properties to theme scales for deterministic token resolution.
4
- */
5
- import type { ThemeScale } from "../types";
6
- /**
7
- * Auto-detects theme scale from CSS property name using pattern matching.
8
- * Used as fallback when property is not in DEFAULT_THEME_MAP.
9
- *
10
- * @param property - CSS property name
11
- * @returns Theme scale name or undefined if no pattern matches
12
- */
13
- export declare function autoDetectScale(property: string): ThemeScale | undefined;
14
- /**
15
- * Gets the theme scale for a CSS property.
16
- * Checks user themeMap first, then default themeMap, then pattern matching.
17
- *
18
- * @param property - CSS property name
19
- * @param userThemeMap - Optional user-provided themeMap override
20
- * @returns Theme scale name or undefined if no mapping found
21
- */
22
- export declare function getScaleForProperty(property: string, userThemeMap?: Record<string, ThemeScale>): ThemeScale | undefined;
@@ -1,97 +0,0 @@
1
- /**
2
- * ThemeMap utilities for property-aware token resolution.
3
- * Maps CSS properties to theme scales for deterministic token resolution.
4
- */
5
- import { DEFAULT_THEME_MAP } from "../constants";
6
- /**
7
- * Auto-detects theme scale from CSS property name using pattern matching.
8
- * Used as fallback when property is not in DEFAULT_THEME_MAP.
9
- *
10
- * @param property - CSS property name
11
- * @returns Theme scale name or undefined if no pattern matches
12
- */
13
- export function autoDetectScale(property) {
14
- // Color properties
15
- if (property.includes("Color") ||
16
- property === "fill" ||
17
- property === "stroke" ||
18
- property === "accentColor" ||
19
- property === "caretColor" ||
20
- property === "border" ||
21
- property === "outline" ||
22
- (property.includes("background") && !property.includes("Size") && !property.includes("Image"))) {
23
- return "colors";
24
- }
25
- // Spacing properties
26
- if (/^(margin|padding|gap|inset|top|right|bottom|left|rowGap|columnGap|gridGap|gridRowGap|gridColumnGap)/.test(property) ||
27
- property.includes("Block") ||
28
- property.includes("Inline")) {
29
- return "space";
30
- }
31
- // Size properties
32
- if (/(width|height|size|basis)$/i.test(property) ||
33
- property.includes("BlockSize") ||
34
- property.includes("InlineSize")) {
35
- return "sizes";
36
- }
37
- // Typography: Font Size
38
- if (property === "fontSize" || (property === "font" && !property.includes("Family"))) {
39
- return "fontSizes";
40
- }
41
- // Typography: Font Family
42
- if (property === "fontFamily" || property.includes("FontFamily")) {
43
- return "fonts";
44
- }
45
- // Typography: Font Weight
46
- if (property === "fontWeight" || property.includes("FontWeight")) {
47
- return "fontWeights";
48
- }
49
- // Typography: Letter Spacing
50
- if (property === "letterSpacing" || property.includes("LetterSpacing")) {
51
- return "letterSpacings";
52
- }
53
- // Border Radius
54
- if (property.includes("Radius") || property.includes("radius")) {
55
- return "radii";
56
- }
57
- // Shadows
58
- if (property.includes("Shadow") ||
59
- property.includes("shadow") ||
60
- property === "filter" ||
61
- property === "backdropFilter") {
62
- return "shadows";
63
- }
64
- // Z-Index
65
- if (property === "zIndex" || property.includes("ZIndex") || property.includes("z-index")) {
66
- return "zIndices";
67
- }
68
- // Opacity
69
- if (property === "opacity" || property.includes("Opacity")) {
70
- return "opacities";
71
- }
72
- // Transitions and animations
73
- if (property.startsWith("transition") ||
74
- property.startsWith("animation") ||
75
- property.includes("Transition") ||
76
- property.includes("Animation")) {
77
- return "transitions";
78
- }
79
- return undefined;
80
- }
81
- /**
82
- * Gets the theme scale for a CSS property.
83
- * Checks user themeMap first, then default themeMap, then pattern matching.
84
- *
85
- * @param property - CSS property name
86
- * @param userThemeMap - Optional user-provided themeMap override
87
- * @returns Theme scale name or undefined if no mapping found
88
- */
89
- export function getScaleForProperty(property, userThemeMap) {
90
- if (userThemeMap && property in userThemeMap) {
91
- return userThemeMap[property];
92
- }
93
- if (property in DEFAULT_THEME_MAP) {
94
- return DEFAULT_THEME_MAP[property];
95
- }
96
- return autoDetectScale(property);
97
- }
@@ -1,13 +0,0 @@
1
- /**
2
- * Theme validation utilities.
3
- * Ensures theme objects only contain approved scales.
4
- */
5
- import type { DefaultTheme } from "../types";
6
- /**
7
- * Validates that a theme object only contains approved scales.
8
- *
9
- * @param theme - Theme object to validate
10
- * @returns Validated theme as DefaultTheme
11
- * @throws Error if theme contains invalid scales (in development)
12
- */
13
- export declare function validateTheme(theme: unknown): DefaultTheme;
@@ -1,36 +0,0 @@
1
- /**
2
- * Theme validation utilities.
3
- * Ensures theme objects only contain approved scales.
4
- */
5
- import { APPROVED_THEME_SCALES } from "../constants";
6
- /**
7
- * Validates that a theme object only contains approved scales.
8
- *
9
- * @param theme - Theme object to validate
10
- * @returns Validated theme as DefaultTheme
11
- * @throws Error if theme contains invalid scales (in development)
12
- */
13
- export function validateTheme(theme) {
14
- if (!theme || typeof theme !== "object" || Array.isArray(theme)) {
15
- throw new Error("[Stoop] Theme must be a non-null object");
16
- }
17
- if (typeof process !== "undefined" && process.env?.NODE_ENV === "production") {
18
- return theme;
19
- }
20
- const themeObj = theme;
21
- const invalidScales = [];
22
- for (const key in themeObj) {
23
- if (key === "media") {
24
- continue;
25
- }
26
- if (!APPROVED_THEME_SCALES.includes(key)) {
27
- invalidScales.push(key);
28
- }
29
- }
30
- if (invalidScales.length > 0) {
31
- const errorMessage = `[Stoop] Theme contains invalid scales: ${invalidScales.join(", ")}. ` +
32
- `Only these scales are allowed: ${APPROVED_THEME_SCALES.join(", ")}`;
33
- throw new Error(errorMessage);
34
- }
35
- return theme;
36
- }