chaincss 1.13.3 → 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 (101) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/LICENSE +2 -3
  3. package/README.md +238 -105
  4. package/dist/cli/commands/build.d.ts +3 -0
  5. package/dist/cli/commands/build.d.ts.map +1 -0
  6. package/dist/cli/commands/compile.d.ts +3 -0
  7. package/dist/cli/commands/compile.d.ts.map +1 -0
  8. package/dist/cli/commands/init.d.ts +5 -0
  9. package/dist/cli/commands/init.d.ts.map +1 -0
  10. package/dist/cli/commands/timeline.d.ts +2 -0
  11. package/dist/cli/commands/timeline.d.ts.map +1 -0
  12. package/dist/cli/commands/watch.d.ts +6 -0
  13. package/dist/cli/commands/watch.d.ts.map +1 -0
  14. package/dist/cli/index.d.ts +2 -0
  15. package/dist/cli/index.d.ts.map +1 -0
  16. package/dist/cli/index.js +5960 -0
  17. package/dist/cli/types.d.ts +51 -0
  18. package/dist/cli/types.d.ts.map +1 -0
  19. package/dist/cli/utils/config-loader.d.ts +8 -0
  20. package/dist/cli/utils/config-loader.d.ts.map +1 -0
  21. package/dist/cli/utils/file-utils.d.ts +9 -0
  22. package/dist/cli/utils/file-utils.d.ts.map +1 -0
  23. package/dist/cli/utils/logger.d.ts +17 -0
  24. package/dist/cli/utils/logger.d.ts.map +1 -0
  25. package/dist/compiler/atomic-optimizer.d.ts +76 -0
  26. package/dist/compiler/atomic-optimizer.d.ts.map +1 -0
  27. package/dist/compiler/btt.d.ts +138 -0
  28. package/dist/compiler/btt.d.ts.map +1 -0
  29. package/dist/compiler/cache-manager.d.ts +20 -0
  30. package/dist/compiler/cache-manager.d.ts.map +1 -0
  31. package/dist/compiler/commonProps.d.ts +2 -0
  32. package/dist/compiler/commonProps.d.ts.map +1 -0
  33. package/dist/compiler/index.d.ts +12 -0
  34. package/dist/compiler/index.d.ts.map +1 -0
  35. package/dist/compiler/index.js +5177 -0
  36. package/dist/compiler/prefixer.d.ts +42 -0
  37. package/dist/compiler/prefixer.d.ts.map +1 -0
  38. package/dist/compiler/theme-contract.d.ts +61 -0
  39. package/dist/compiler/theme-contract.d.ts.map +1 -0
  40. package/dist/compiler/tokens.d.ts +52 -0
  41. package/dist/compiler/tokens.d.ts.map +1 -0
  42. package/dist/compiler/types.d.ts +57 -0
  43. package/dist/compiler/types.d.ts.map +1 -0
  44. package/dist/core/compiler.d.ts +32 -0
  45. package/dist/core/compiler.d.ts.map +1 -0
  46. package/dist/core/constants.d.ts +129 -0
  47. package/dist/core/constants.d.ts.map +1 -0
  48. package/dist/core/index.d.ts +4 -0
  49. package/dist/core/index.d.ts.map +1 -0
  50. package/dist/core/types.d.ts +88 -0
  51. package/dist/core/types.d.ts.map +1 -0
  52. package/dist/core/utils.d.ts +37 -0
  53. package/dist/core/utils.d.ts.map +1 -0
  54. package/dist/index.d.ts +13 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +5667 -0
  57. package/dist/plugins/vite.d.ts +11 -0
  58. package/dist/plugins/vite.d.ts.map +1 -0
  59. package/dist/plugins/vite.js +25839 -0
  60. package/dist/plugins/webpack.d.ts +45 -0
  61. package/dist/plugins/webpack.d.ts.map +1 -0
  62. package/dist/plugins/webpack.js +107 -0
  63. package/dist/runtime/hmr.d.ts +3 -0
  64. package/dist/runtime/hmr.d.ts.map +1 -0
  65. package/dist/runtime/index.d.ts +15 -0
  66. package/dist/runtime/index.d.ts.map +1 -0
  67. package/dist/runtime/index.js +552 -0
  68. package/dist/runtime/injector.d.ts +85 -0
  69. package/dist/runtime/injector.d.ts.map +1 -0
  70. package/dist/runtime/react.d.ts +54 -0
  71. package/dist/runtime/react.d.ts.map +1 -0
  72. package/dist/runtime/react.js +270 -0
  73. package/dist/runtime/types.d.ts +45 -0
  74. package/dist/runtime/types.d.ts.map +1 -0
  75. package/dist/runtime/utils.d.ts +62 -0
  76. package/dist/runtime/utils.d.ts.map +1 -0
  77. package/dist/runtime/vue.d.ts +52 -0
  78. package/dist/runtime/vue.d.ts.map +1 -0
  79. package/dist/runtime/vue.js +232 -0
  80. package/package.json +90 -119
  81. package/browser/commonProps.js +0 -14
  82. package/browser/index.js +0 -3
  83. package/browser/react-hooks.js +0 -162
  84. package/browser/rtt.js +0 -400
  85. package/browser/vue-composables.js +0 -200
  86. package/node/atomic-optimizer.js +0 -526
  87. package/node/btt.js +0 -1009
  88. package/node/cache-manager.js +0 -56
  89. package/node/chaincss.js +0 -642
  90. package/node/index.js +0 -2
  91. package/node/loaders/chaincss-loader.js +0 -62
  92. package/node/plugins/next-plugin.js +0 -120
  93. package/node/plugins/vite-plugin.js +0 -383
  94. package/node/plugins/webpack-plugin.js +0 -41
  95. package/node/prefixer.js +0 -237
  96. package/node/strVal.js +0 -92
  97. package/node/theme-validator.js +0 -32
  98. package/shared/theme-contract.js +0 -98
  99. package/shared/tokens.cjs +0 -256
  100. package/shared/tokens.mjs +0 -320
  101. package/types.d.ts +0 -325
@@ -1,526 +0,0 @@
1
- const crypto = require('crypto');
2
- const path = require('path');
3
- const fs = require('fs');
4
-
5
- function hashKey(key) {
6
- return crypto.createHash('sha1').update(key).digest('hex').slice(0, 6);
7
- }
8
-
9
- function kebab(s) {
10
- return s.replace(/([A-Z])/g, '-$1').toLowerCase();
11
- }
12
-
13
- class AtomicOptimizer {
14
- constructor(options = {}) {
15
- // Ensure arrays are arrays (fix for config merging issues)
16
- if (options.alwaysAtomic && !Array.isArray(options.alwaysAtomic)) {
17
- options.alwaysAtomic = Object.values(options.alwaysAtomic);
18
- }
19
- if (options.neverAtomic && !Array.isArray(options.neverAtomic)) {
20
- options.neverAtomic = Object.values(options.neverAtomic);
21
- }
22
-
23
- this.options = {
24
- enabled: true,
25
- threshold: 3,
26
- naming: 'hash',
27
- cache: true,
28
- cachePath: './.chaincss-cache',
29
- minify: true,
30
- mode: 'hybrid',
31
- outputStrategy: 'component-first',
32
- alwaysAtomic: [],
33
- neverAtomic: [
34
- 'content', 'animation', 'transition', 'keyframes',
35
- 'counterIncrement', 'counterReset'
36
- ],
37
- frameworkOutput: {
38
- react: false,
39
- vue: false,
40
- vanilla: true
41
- },
42
- preserveSelectors: false,
43
- verbose: false,
44
- ...options
45
- };
46
-
47
- this.usageCount = new Map();
48
- this.atomicClasses = new Map();
49
- this.componentClassMap = new Map();
50
- this.stats = {
51
- totalStyles: 0,
52
- atomicStyles: 0,
53
- standardStyles: 0,
54
- uniqueProperties: 0,
55
- savings: 0
56
- };
57
-
58
- if (this.options.cache) {
59
- this.loadCache();
60
- }
61
- }
62
-
63
- // ============================================================================
64
- // Cache Management
65
- // ============================================================================
66
-
67
- loadCache() {
68
- try {
69
- if (!fs.existsSync(this.options.cachePath)) return;
70
-
71
- const data = JSON.parse(fs.readFileSync(this.options.cachePath, 'utf8'));
72
-
73
- if (data.version !== '1.0.0') {
74
- //if (this.options.verbose) console.log('Cache version mismatch, creating new cache');
75
- return;
76
- }
77
-
78
- if (data.config?.threshold !== this.options.threshold) {
79
- //if (this.options.verbose) console.log(`Cache threshold (${data.config?.threshold}) differs from current (${this.options.threshold})`);
80
- return;
81
- }
82
-
83
- this.atomicClasses = new Map(data.atomicClasses || []);
84
- this.componentClassMap = new Map(data.componentClassMap || []);
85
- this.stats = data.stats || this.stats;
86
-
87
- const cacheTime = new Date(data.timestamp).toLocaleString();
88
-
89
- } catch (err) {
90
- console.log('Could not load cache:', err.message);
91
- }
92
- }
93
-
94
- saveCache() {
95
- if (!this.options.cache) return;
96
-
97
- try {
98
- const cacheDir = path.dirname(this.options.cachePath);
99
- if (!fs.existsSync(cacheDir)) {
100
- fs.mkdirSync(cacheDir, { recursive: true });
101
- }
102
-
103
- const cache = {
104
- version: '1.0.0',
105
- timestamp: Date.now(),
106
- atomicClasses: Array.from(this.atomicClasses.entries()),
107
- componentClassMap: Array.from(this.componentClassMap.entries()),
108
- stats: this.stats,
109
- config: {
110
- threshold: this.options.threshold,
111
- naming: this.options.naming,
112
- mode: this.options.mode,
113
- outputStrategy: this.options.outputStrategy
114
- }
115
- };
116
-
117
- fs.writeFileSync(this.options.cachePath, JSON.stringify(cache, null, 2), 'utf8');
118
- } catch (err) {
119
- console.log('Could not save cache:', err.message);
120
- }
121
- }
122
-
123
- // ============================================================================
124
- // Style Tracking
125
- // ============================================================================
126
-
127
- trackStyles(styles) {
128
- const styleArray = Array.isArray(styles) ? styles : Object.values(styles);
129
-
130
- for (const style of styleArray) {
131
- if (!style || !style.selectors) continue;
132
-
133
- for (const [prop, value] of Object.entries(style)) {
134
- if (prop === 'selectors' || prop === 'atRules') continue;
135
-
136
- if (prop === 'hover' && typeof value === 'object') {
137
- for (const [hoverProp, hoverValue] of Object.entries(value)) {
138
- this.incrementUsage(hoverProp, hoverValue);
139
- }
140
- } else {
141
- this.incrementUsage(prop, value);
142
- }
143
- }
144
- }
145
-
146
- this.stats.uniqueProperties = this.usageCount.size;
147
- }
148
-
149
- incrementUsage(prop, value) {
150
- const key = `${prop}:${value}`;
151
- const count = (this.usageCount.get(key) || 0) + 1;
152
- this.usageCount.set(key, count);
153
- this.stats.totalStyles++;
154
-
155
- if (this.atomicClasses.has(key)) {
156
- this.atomicClasses.get(key).usageCount = count;
157
- }
158
- }
159
-
160
- shouldBeAtomic(prop, value) {
161
- if (this.options.mode === 'standard') return false;
162
- if (this.options.mode === 'atomic') return true;
163
-
164
- if (this.options.neverAtomic.includes(prop)) return false;
165
- if (this.options.alwaysAtomic.includes(prop)) return true;
166
-
167
- const criticalProps = ['position', 'display', 'flex', 'grid', 'zIndex', 'top', 'left', 'right', 'bottom'];
168
- const isCritical = criticalProps.includes(prop);
169
-
170
- const key = `${prop}:${value}`;
171
- const usage = this.usageCount.get(key) || 0;
172
-
173
- if (isCritical && usage < this.options.threshold * 2) return false;
174
- return usage >= this.options.threshold;
175
- }
176
-
177
- generateClassName(prop, value) {
178
- const key = `${prop}:${value}`;
179
-
180
- if (this.options.naming === 'hash') {
181
- return `c_${hashKey(key)}`;
182
- }
183
-
184
- const kebabProp = kebab(prop);
185
- const safeValue = String(value).replace(/[^a-z0-9_-]/gi, '-').slice(0, 30);
186
- return `${kebabProp}-${safeValue}`;
187
- }
188
-
189
- getOrCreateAtomic(prop, value) {
190
- const key = `${prop}:${value}`;
191
-
192
- if (!this.atomicClasses.has(key)) {
193
- const className = this.generateClassName(prop, value);
194
- this.atomicClasses.set(key, {
195
- className,
196
- prop,
197
- value,
198
- usageCount: this.usageCount.get(key) || 0
199
- });
200
- this.stats.atomicStyles++;
201
- }
202
-
203
- return this.atomicClasses.get(key).className;
204
- }
205
-
206
- getKeyFromClassName(className) {
207
- for (const [key, atomic] of this.atomicClasses) {
208
- if (atomic.className === className) return key;
209
- }
210
- return null;
211
- }
212
-
213
- // ============================================================================
214
- // CSS Generation
215
- // ============================================================================
216
-
217
- generateAtomicCSS() {
218
- let css = '';
219
- const sortedClasses = Array.from(this.atomicClasses.values())
220
- .sort((a, b) => b.usageCount - a.usageCount);
221
-
222
- for (const atomic of sortedClasses) {
223
- const kebabProp = kebab(atomic.prop);
224
- if (this.options.minify) {
225
- css += `.${atomic.className}{${kebabProp}:${atomic.value}}`;
226
- } else {
227
- css += `.${atomic.className} {\n ${kebabProp}: ${atomic.value};\n}\n`;
228
- }
229
- }
230
-
231
- // Debug: Log atomic CSS generation
232
- if (this.options.verbose && css) {
233
- //console.log(` Generated ${this.atomicClasses.size} atomic classes (${css.length} bytes)`);
234
- if (css.length > 0) {
235
- //console.log(`First atomic class: ${css.substring(0, 50)}...`);
236
- }
237
- }
238
-
239
- return css;
240
- }
241
-
242
- generateComponentCSS(style, selectors) {
243
- const atomicClasses = [];
244
- const hoverAtomicClasses = [];
245
-
246
- // Track which properties become atomic
247
- for (const [prop, value] of Object.entries(style)) {
248
- if (prop === 'selectors' || prop === 'atRules') continue;
249
-
250
- if (prop === 'hover' && typeof value === 'object') {
251
- for (const [hoverProp, hoverValue] of Object.entries(value)) {
252
- if (this.shouldBeAtomic(hoverProp, hoverValue)) {
253
- hoverAtomicClasses.push(this.getOrCreateAtomic(hoverProp, hoverValue));
254
- }
255
- }
256
- } else if (this.shouldBeAtomic(prop, value)) {
257
- atomicClasses.push(this.getOrCreateAtomic(prop, value));
258
- }
259
- }
260
-
261
- let componentCSS = '';
262
- const selectorStr = selectors.join(', ');
263
-
264
- // COMPONENT-FIRST STRATEGY (Default)
265
- if (this.options.outputStrategy === 'component-first') {
266
- // Include ALL properties in component CSS
267
- const allStyles = {};
268
-
269
- // Add all properties (both atomic and non-atomic)
270
- for (const [prop, value] of Object.entries(style)) {
271
- if (prop !== 'selectors' && prop !== 'atRules' && prop !== 'hover') {
272
- allStyles[prop] = value;
273
- }
274
- }
275
-
276
- // Generate CSS with ALL properties
277
- if (Object.keys(allStyles).length > 0) {
278
- if (this.options.minify) {
279
- componentCSS += `${selectorStr}{`;
280
- for (const [prop, value] of Object.entries(allStyles)) {
281
- const kebabProp = kebab(prop);
282
- componentCSS += `${kebabProp}:${value};`;
283
- }
284
- componentCSS += `}`;
285
- } else {
286
- componentCSS += `${selectorStr} {\n`;
287
- for (const [prop, value] of Object.entries(allStyles)) {
288
- const kebabProp = kebab(prop);
289
- componentCSS += ` ${kebabProp}: ${value};\n`;
290
- }
291
- componentCSS += `}\n`;
292
- }
293
- }
294
-
295
- // Add hover styles (all hover properties)
296
- if (style.hover && typeof style.hover === 'object') {
297
- const allHoverStyles = {};
298
- for (const [prop, value] of Object.entries(style.hover)) {
299
- allHoverStyles[prop] = value;
300
- }
301
-
302
- if (Object.keys(allHoverStyles).length > 0) {
303
- if (this.options.minify) {
304
- componentCSS += `${selectorStr}:hover{`;
305
- for (const [prop, value] of Object.entries(allHoverStyles)) {
306
- const kebabProp = kebab(prop);
307
- componentCSS += `${kebabProp}:${value};`;
308
- }
309
- componentCSS += `}`;
310
- } else {
311
- componentCSS += `${selectorStr}:hover {\n`;
312
- for (const [prop, value] of Object.entries(allHoverStyles)) {
313
- const kebabProp = kebab(prop);
314
- componentCSS += ` ${kebabProp}: ${value};\n`;
315
- }
316
- componentCSS += `}\n`;
317
- }
318
- }
319
- }
320
- }
321
-
322
- // UTILITY-FIRST STRATEGY (Advanced)
323
- else {
324
- const standardStyles = {};
325
- const hoverStandardStyles = {};
326
-
327
- for (const [prop, value] of Object.entries(style)) {
328
- if (prop === 'selectors' || prop === 'atRules') continue;
329
-
330
- if (prop === 'hover' && typeof value === 'object') {
331
- for (const [hoverProp, hoverValue] of Object.entries(value)) {
332
- if (!this.shouldBeAtomic(hoverProp, hoverValue)) {
333
- hoverStandardStyles[hoverProp] = hoverValue;
334
- }
335
- }
336
- } else if (!this.shouldBeAtomic(prop, value)) {
337
- standardStyles[prop] = value;
338
- }
339
- }
340
-
341
- if (Object.keys(standardStyles).length > 0) {
342
- if (this.options.minify) {
343
- componentCSS += `${selectorStr}{`;
344
- for (const [prop, value] of Object.entries(standardStyles)) {
345
- const kebabProp = kebab(prop);
346
- componentCSS += `${kebabProp}:${value};`;
347
- }
348
- componentCSS += `}`;
349
- } else {
350
- componentCSS += `${selectorStr} {\n`;
351
- for (const [prop, value] of Object.entries(standardStyles)) {
352
- const kebabProp = kebab(prop);
353
- componentCSS += ` ${kebabProp}: ${value};\n`;
354
- }
355
- componentCSS += `}\n`;
356
- }
357
- }
358
-
359
- if (Object.keys(hoverStandardStyles).length > 0) {
360
- if (this.options.minify) {
361
- componentCSS += `${selectorStr}:hover{`;
362
- for (const [prop, value] of Object.entries(hoverStandardStyles)) {
363
- const kebabProp = kebab(prop);
364
- componentCSS += `${kebabProp}:${value};`;
365
- }
366
- componentCSS += `}`;
367
- } else {
368
- componentCSS += `${selectorStr}:hover {\n`;
369
- for (const [prop, value] of Object.entries(hoverStandardStyles)) {
370
- const kebabProp = kebab(prop);
371
- componentCSS += ` ${kebabProp}: ${value};\n`;
372
- }
373
- componentCSS += `}\n`;
374
- }
375
- }
376
- }
377
-
378
- return {
379
- css: componentCSS,
380
- atomicClasses,
381
- hoverAtomicClasses
382
- };
383
- }
384
-
385
- // ============================================================================
386
- // Main Optimize Method
387
- // ============================================================================
388
-
389
- optimize(stylesInput) {
390
- if (!this.options.enabled) {
391
- return {
392
- css: '',
393
- map: {},
394
- stats: this.getStats(),
395
- atomicCSS: ''
396
- };
397
- }
398
-
399
- // Reset stats
400
- this.stats = {
401
- totalStyles: 0,
402
- atomicStyles: 0,
403
- standardStyles: 0,
404
- uniqueProperties: 0,
405
- savings: 0
406
- };
407
- this.usageCount.clear();
408
- this.componentClassMap.clear();
409
-
410
- // Normalize input
411
- let styleArray = [];
412
- if (Array.isArray(stylesInput)) {
413
- styleArray = stylesInput;
414
- } else if (typeof stylesInput === 'object') {
415
- styleArray = Object.values(stylesInput).filter(v => v && typeof v === 'object');
416
- }
417
-
418
- if (styleArray.length === 0) {
419
- return { css: '', map: {}, stats: this.getStats(), atomicCSS: '', componentCSS: '' };
420
- }
421
-
422
- // FIRST: Track usage counts
423
- this.trackStyles(styleArray);
424
-
425
- // SECOND: Generate component CSS (this populates atomicClasses via getOrCreateAtomic)
426
- let componentCSS = '';
427
- const classMap = {};
428
-
429
- for (const style of styleArray) {
430
- if (!style || !style.selectors) continue;
431
-
432
- const selectors = style.selectors;
433
- const selectorKey = selectors.join(', ');
434
- const { css, atomicClasses, hoverAtomicClasses } = this.generateComponentCSS(style, selectors);
435
- componentCSS += css;
436
-
437
- if (this.options.outputStrategy === 'utility-first') {
438
- if (atomicClasses.length > 0) {
439
- classMap[selectorKey] = atomicClasses.join(' ');
440
- }
441
- if (hoverAtomicClasses.length > 0) {
442
- classMap[`${selectorKey}:hover`] = hoverAtomicClasses.join(' ');
443
- }
444
- }
445
-
446
- this.componentClassMap.set(selectorKey, {
447
- atomicClasses,
448
- hoverAtomicClasses,
449
- selectors
450
- });
451
- }
452
-
453
- // THIRD: Generate atomic CSS (now atomicClasses is populated!)
454
- const atomicCSS = this.generateAtomicCSS();
455
-
456
- // Combine CSS
457
- const finalCSS = atomicCSS + componentCSS;
458
-
459
- // Log stats if verbose
460
- if (this.options.verbose) {
461
- const stats = this.getStats();
462
- //console.log(` Atomic Optimization Stats:`);
463
- //console.log(` Output strategy: ${this.options.outputStrategy}`);
464
- //console.log(` Total styles tracked: ${stats.totalStyles}`);
465
- //console.log(` Atomic classes created: ${this.atomicClasses.size}`);
466
- //console.log(` Atomic CSS length: ${atomicCSS.length} bytes`);
467
- //console.log(` Component CSS length: ${componentCSS.length} bytes`);
468
- //console.log(` Total CSS length: ${finalCSS.length} bytes`);
469
- //console.log(` Savings: ${stats.savings}`);
470
-
471
- if (atomicCSS.length === 0 && this.atomicClasses.size > 0) {
472
- //console.log(` WARNING: ${this.atomicClasses.size} atomic classes exist but generated CSS is empty!`);
473
- }
474
- }
475
-
476
- // Save cache
477
- if (this.options.cache) {
478
- this.saveCache();
479
- }
480
-
481
- return {
482
- css: finalCSS,
483
- map: classMap,
484
- stats: this.getStats(),
485
- atomicCSS: atomicCSS,
486
- componentCSS: componentCSS,
487
- componentMap: this.componentClassMap
488
- };
489
- }
490
-
491
- getStats() {
492
- const savings = this.stats.totalStyles > 0
493
- ? ((this.stats.totalStyles - this.stats.atomicStyles) / this.stats.totalStyles * 100).toFixed(1)
494
- : 0;
495
-
496
- return {
497
- totalStyles: this.stats.totalStyles,
498
- atomicStyles: this.stats.atomicStyles,
499
- standardStyles: this.stats.standardStyles,
500
- uniqueProperties: this.stats.uniqueProperties,
501
- savings: `${savings}%`
502
- };
503
- }
504
-
505
- getAtomicClass(prop, value) {
506
- const key = `${prop}:${value}`;
507
- const atomic = this.atomicClasses.get(key);
508
- return atomic ? atomic.className : null;
509
- }
510
-
511
- getAllAtomicClasses() {
512
- return Array.from(this.atomicClasses.values());
513
- }
514
-
515
- clearCache() {
516
- this.atomicClasses.clear();
517
- this.componentClassMap.clear();
518
- this.usageCount.clear();
519
- if (this.options.cache && fs.existsSync(this.options.cachePath)) {
520
- fs.unlinkSync(this.options.cachePath);
521
- }
522
- //console.log('Atomic optimizer cache cleared');
523
- }
524
- }
525
-
526
- module.exports = { AtomicOptimizer };