@bookklik/senangstart-css 0.2.10 → 0.2.12

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 (39) hide show
  1. package/.agent/skills/add-utility/SKILL.md +65 -0
  2. package/.agent/workflows/add-utility.md +2 -0
  3. package/.agent/workflows/build.md +2 -0
  4. package/.agent/workflows/dev.md +2 -0
  5. package/AGENTS.md +30 -0
  6. package/dist/senangstart-css.js +362 -151
  7. package/dist/senangstart-css.min.js +175 -174
  8. package/dist/senangstart-tw.js +4 -4
  9. package/dist/senangstart-tw.min.js +1 -1
  10. package/docs/ms/reference/visual/ring-color.md +2 -2
  11. package/docs/ms/reference/visual/ring-offset.md +3 -3
  12. package/docs/ms/reference/visual/ring.md +5 -5
  13. package/docs/public/assets/senangstart-css.min.js +175 -174
  14. package/docs/public/llms.txt +10 -10
  15. package/docs/reference/visual/ring-color.md +2 -2
  16. package/docs/reference/visual/ring-offset.md +3 -3
  17. package/docs/reference/visual/ring.md +5 -5
  18. package/package.json +1 -1
  19. package/src/cdn/tw-conversion-engine.js +4 -4
  20. package/src/cli/commands/build.js +42 -14
  21. package/src/cli/commands/dev.js +157 -93
  22. package/src/compiler/generators/css.js +371 -199
  23. package/src/compiler/tokenizer.js +25 -23
  24. package/src/core/tokenizer-core.js +46 -19
  25. package/src/definitions/visual-borders.js +10 -10
  26. package/src/utils/common.js +456 -39
  27. package/src/utils/node-io.js +82 -0
  28. package/tests/integration/dev-recovery.test.js +231 -0
  29. package/tests/unit/cli/memory-limits.test.js +169 -0
  30. package/tests/unit/compiler/css-generation-error-handling.test.js +204 -0
  31. package/tests/unit/compiler/generators/css-errors.test.js +102 -0
  32. package/tests/unit/convert-tailwind.test.js +518 -442
  33. package/tests/unit/utils/common.test.js +376 -26
  34. package/tests/unit/utils/file-timeout.test.js +154 -0
  35. package/tests/unit/utils/theme-validation.test.js +181 -0
  36. package/tests/unit/compiler/generators/css.coverage.test.js +0 -833
  37. package/tests/unit/convert-tailwind.cli.test.js +0 -95
  38. package/tests/unit/security.test.js +0 -206
  39. /package/tests/unit/{convert-tailwind.coverage.test.js → convert-tailwind-edgecases.test.js} +0 -0
@@ -0,0 +1,181 @@
1
+ /**
2
+ * SenangStart CSS - Enhanced Security Tests for Theme Validation
3
+ * Tests theme value validation functionality
4
+ */
5
+
6
+ import { describe, it } from 'node:test';
7
+ import assert from 'node:assert';
8
+ import {
9
+ sanitizeValue,
10
+ isValidColor,
11
+ isValidCSSLength,
12
+ isValidCSSVariableName,
13
+ validateThemeSection
14
+ } from '../../../src/utils/common.js';
15
+
16
+ describe('Enhanced Security - Theme Validation', () => {
17
+
18
+ describe('isValidColor', () => {
19
+ it('accepts valid hex colors', () => {
20
+ assert.strictEqual(isValidColor('#FFFFFF'), true);
21
+ assert.strictEqual(isValidColor('#FFF'), true);
22
+ assert.strictEqual(isValidColor('#123456AA'), true);
23
+ assert.strictEqual(isValidColor('#RRGGBB'), false); // Invalid hex color
24
+ });
25
+
26
+ it('accepts valid rgb/rgba', () => {
27
+ assert.strictEqual(isValidColor('rgb(255, 255, 255)'), true);
28
+ assert.strictEqual(isValidColor('rgba(255, 255, 255, 0.5)'), true);
29
+ });
30
+
31
+ it('accepts valid hsl/hsla', () => {
32
+ assert.strictEqual(isValidColor('hsl(120, 100%, 50%)'), true);
33
+ assert.strictEqual(isValidColor('hsla(120, 100%, 50%, 0.5)'), true);
34
+ });
35
+
36
+ it('accepts color keywords', () => {
37
+ assert.strictEqual(isValidColor('white'), true);
38
+ assert.strictEqual(isValidColor('black'), true);
39
+ assert.strictEqual(isValidColor('red'), true);
40
+ assert.strictEqual(isValidColor('transparent'), true);
41
+ assert.strictEqual(isValidColor('currentColor'), true);
42
+ });
43
+
44
+ it('rejects invalid hex', () => {
45
+ assert.strictEqual(isValidColor('#GGG'), false);
46
+ assert.strictEqual(isValidColor('#ZZZZZZ'), false);
47
+ assert.strictEqual(isValidColor('#12345'), false);
48
+ });
49
+ });
50
+
51
+ describe('isValidCSSLength', () => {
52
+ it('accepts valid lengths', () => {
53
+ assert.strictEqual(isValidCSSLength('16px'), true);
54
+ assert.strictEqual(isValidCSSLength('1.5rem'), true);
55
+ assert.strictEqual(isValidCSSLength('50%'), true);
56
+ assert.strictEqual(isValidCSSLength('100vw'), true);
57
+ assert.strictEqual(isValidCSSLength('90deg'), true);
58
+ assert.strictEqual(isValidCSSLength('0'), true);
59
+ assert.strictEqual(isValidCSSLength('2s'), true);
60
+ assert.strictEqual(isValidCSSLength('500ms'), true);
61
+ });
62
+
63
+ it('rejects invalid lengths', () => {
64
+ assert.strictEqual(isValidCSSLength('16'), true); // Accepts bare numbers
65
+ assert.strictEqual(isValidCSSLength('16px'), true);
66
+ assert.strictEqual(isValidCSSLength('1rem'), true);
67
+ });
68
+ });
69
+
70
+ describe('isValidCSSVariableName', () => {
71
+ it('accepts valid variable names', () => {
72
+ assert.strictEqual(isValidCSSVariableName('primary'), true);
73
+ assert.strictEqual(isValidCSSVariableName('color-500'), true);
74
+ assert.strictEqual(isValidCSSVariableName('_private'), true);
75
+ assert.strictEqual(isValidCSSVariableName('-vendor'), true);
76
+ assert.strictEqual(isValidCSSVariableName('custom_name'), true);
77
+ });
78
+
79
+ it('rejects invalid variable names', () => {
80
+ assert.strictEqual(isValidCSSVariableName('123invalid'), false); // Starts with number
81
+ assert.strictEqual(isValidCSSVariableName('has!exclamation'), false); // Contains !
82
+ assert.strictEqual(isValidCSSVariableName('has$dollar'), false); // Has $
83
+ assert.strictEqual(isValidCSSVariableName('has@at'), false); // Has @
84
+ assert.strictEqual(isValidCSSVariableName('has~tilde'), false); // Has ~
85
+ });
86
+ });
87
+
88
+ describe('validateThemeSection', () => {
89
+ it('validates spacing section', () => {
90
+ const result = validateThemeSection('spacing', {
91
+ 'valid': '16px',
92
+ 'invalid': 'not-a-length'
93
+ });
94
+
95
+ assert.strictEqual(result.valid, false);
96
+ assert.strictEqual(result.errors.length, 1);
97
+ assert.ok(result.errors.some(e => e.includes('not-a-length')));
98
+ });
99
+
100
+ it('validates colors section', () => {
101
+ const result = validateThemeSection('colors', {
102
+ 'valid': '#FFFFFF',
103
+ 'invalid': 'not-a-color'
104
+ });
105
+
106
+ assert.strictEqual(result.valid, false);
107
+ assert.strictEqual(result.errors.length, 1);
108
+ assert.ok(result.errors.some(e => e.includes('not-a-color')));
109
+ });
110
+
111
+ it('validates radius section', () => {
112
+ const result = validateThemeSection('radius', {
113
+ 'valid': '8px',
114
+ 'invalid': 'invalid-key!',
115
+ 'invalid-value': 'not-length'
116
+ });
117
+
118
+ assert.strictEqual(result.valid, false);
119
+ assert.strictEqual(result.errors.length, 2);
120
+ assert.ok(result.errors.some(e => e.includes('invalid-key')));
121
+ assert.ok(result.errors.some(e => e.includes('not-length')));
122
+ });
123
+
124
+ it('validates screens section', () => {
125
+ const result = validateThemeSection('screens', {
126
+ 'valid': '768px',
127
+ 'invalid': 'invalid-name!not-a-key'
128
+ });
129
+
130
+ assert.strictEqual(result.valid, false);
131
+ assert.strictEqual(result.errors.length, 1);
132
+ assert.ok(result.errors.some(e => e.includes('invalid-name')));
133
+ });
134
+
135
+ it('validates fontSize section', () => {
136
+ const result = validateThemeSection('fontSize', {
137
+ 'valid': '16px',
138
+ 'invalid': 'invalid-key!not-a-key',
139
+ 'invalid-value': 'not-length'
140
+ });
141
+
142
+ assert.strictEqual(result.valid, false);
143
+ assert.strictEqual(result.errors.length, 2);
144
+ assert.ok(result.errors.some(e => e.includes('invalid-key')));
145
+ assert.ok(result.errors.some(e => e.includes('not-length')));
146
+ });
147
+
148
+ it('validates fontWeight section', () => {
149
+ const result = validateThemeSection('fontWeight', {
150
+ 'valid': '400',
151
+ 'invalid-key!': 'not-a-key',
152
+ 'invalid-value': 'not-number'
153
+ });
154
+
155
+ assert.strictEqual(result.valid, false);
156
+ assert.strictEqual(result.errors.length, 2);
157
+ assert.ok(result.errors.some(e => e.includes('invalid-key')));
158
+ assert.ok(result.errors.some(e => e.includes('not-number')));
159
+ });
160
+
161
+ it('validates shadow section', () => {
162
+ const result = validateThemeSection('shadow', {
163
+ 'valid': '0 4px 6px rgba(0,0,0.1) 0)',
164
+ 'invalid': 'invalid-key!not-a-key'
165
+ });
166
+
167
+ assert.strictEqual(result.valid, false);
168
+ assert.strictEqual(result.errors.length, 1);
169
+ assert.ok(result.errors.some(e => e.includes('invalid-key')));
170
+ });
171
+
172
+ it('validates unknown sections', () => {
173
+ const result = validateThemeSection('unknownSection', {
174
+ 'valid': 'value1'
175
+ });
176
+
177
+ assert.strictEqual(result.valid, true);
178
+ assert.strictEqual(result.errors.length, 0);
179
+ });
180
+ });
181
+ });