snow-flow 3.5.14 ā 3.5.15
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.
|
@@ -31,6 +31,8 @@ export interface LocalFile {
|
|
|
31
31
|
currentContent?: string;
|
|
32
32
|
isModified: boolean;
|
|
33
33
|
fieldMapping?: FieldMapping;
|
|
34
|
+
existedBefore?: boolean;
|
|
35
|
+
previousContent?: string;
|
|
34
36
|
}
|
|
35
37
|
export declare class ArtifactLocalSync {
|
|
36
38
|
private baseDir;
|
|
@@ -84,9 +86,21 @@ export declare class ArtifactLocalSync {
|
|
|
84
86
|
*/
|
|
85
87
|
private validateES5;
|
|
86
88
|
/**
|
|
87
|
-
* Strip headers/footers we added -
|
|
89
|
+
* Strip headers/footers we added - FIXED to use actual fieldMapping wrappers
|
|
88
90
|
*/
|
|
89
91
|
private stripAddedWrappers;
|
|
92
|
+
/**
|
|
93
|
+
* Create regex pattern from wrapper text, handling placeholders
|
|
94
|
+
*/
|
|
95
|
+
private escapeRegexAndCreatePattern;
|
|
96
|
+
/**
|
|
97
|
+
* Fallback: Strip generic comments when no fieldMapping available
|
|
98
|
+
*/
|
|
99
|
+
private stripGenericComments;
|
|
100
|
+
/**
|
|
101
|
+
* INTELLIGENT WRAPPER DETECTION: Check if content actually needs wrappers
|
|
102
|
+
*/
|
|
103
|
+
private needsWrappers;
|
|
90
104
|
/**
|
|
91
105
|
* Sanitize filename for filesystem
|
|
92
106
|
*/
|
|
@@ -127,6 +127,7 @@ class ArtifactLocalSync {
|
|
|
127
127
|
throw new Error(`Unsupported artifact type: ${tableName}. Supported types: ${Object.keys(artifact_registry_1.ARTIFACT_REGISTRY).join(', ')}`);
|
|
128
128
|
}
|
|
129
129
|
console.log(`\nš Pulling ${config.displayName} (${sys_id}) to local files...`);
|
|
130
|
+
console.log(`š Snow-Flow v3.5.15 - Intelligent Wrapper System Active`);
|
|
130
131
|
// Get timeout configuration
|
|
131
132
|
const timeoutConfig = (0, mcp_timeout_config_js_1.getMCPTimeoutConfig)();
|
|
132
133
|
// Use smart fetcher for known types, otherwise direct query
|
|
@@ -186,9 +187,17 @@ class ArtifactLocalSync {
|
|
|
186
187
|
.replace('{name}', sanitizedName)
|
|
187
188
|
.replace('{api_name}', artifactData.api_name || sanitizedName)
|
|
188
189
|
.replace('{short_description}', artifactData.short_description || sanitizedName);
|
|
189
|
-
//
|
|
190
|
-
|
|
191
|
-
|
|
190
|
+
// INTELLIGENT WRAPPER SYSTEM: Only add wrappers if actually needed
|
|
191
|
+
let header = '';
|
|
192
|
+
let footer = '';
|
|
193
|
+
if (mapping.wrapperHeader && mapping.wrapperFooter && this.needsWrappers(processedContent, mapping)) {
|
|
194
|
+
header = this.replacePlaceholders(mapping.wrapperHeader, artifactData);
|
|
195
|
+
footer = this.replacePlaceholders(mapping.wrapperFooter, artifactData);
|
|
196
|
+
console.log(` š§ Adding wrappers for ${filename} (content needs them)`);
|
|
197
|
+
}
|
|
198
|
+
else if (mapping.wrapperHeader && mapping.wrapperFooter) {
|
|
199
|
+
console.log(` ā
Skipping wrappers for ${filename} (content is already wrapped)`);
|
|
200
|
+
}
|
|
192
201
|
const file = this.createLocalFile(artifactPath, `${filename}.${mapping.fileExtension}`, processedContent, mapping.serviceNowField, mapping.fileExtension, header, footer);
|
|
193
202
|
file.fieldMapping = mapping;
|
|
194
203
|
files.push(file);
|
|
@@ -370,6 +379,31 @@ class ArtifactLocalSync {
|
|
|
370
379
|
createLocalFile(dirPath, filename, content, field, type, header = '', footer = '') {
|
|
371
380
|
const filePath = path.join(dirPath, filename);
|
|
372
381
|
const fullContent = header + content + footer;
|
|
382
|
+
// CRITICAL FIX: Check if file exists and handle overwrites intelligently
|
|
383
|
+
let fileExists = false;
|
|
384
|
+
let existingContent = '';
|
|
385
|
+
if (fs.existsSync(filePath)) {
|
|
386
|
+
fileExists = true;
|
|
387
|
+
existingContent = fs.readFileSync(filePath, 'utf8');
|
|
388
|
+
// Check if existing content is the same (avoid unnecessary writes)
|
|
389
|
+
if (existingContent === fullContent) {
|
|
390
|
+
console.log(` š Unchanged: ${filename} (identical content, skipping write)`);
|
|
391
|
+
return {
|
|
392
|
+
filename,
|
|
393
|
+
path: filePath,
|
|
394
|
+
field,
|
|
395
|
+
type: type,
|
|
396
|
+
originalContent: content,
|
|
397
|
+
isModified: false
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
console.log(` š Overwriting: ${filename}`);
|
|
401
|
+
console.log(` ā¹ļø File size: ${existingContent.length} ā ${fullContent.length} chars`);
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
console.log(` š Creating: ${filename}`);
|
|
405
|
+
}
|
|
406
|
+
// Write the file
|
|
373
407
|
fs.writeFileSync(filePath, fullContent, 'utf8');
|
|
374
408
|
return {
|
|
375
409
|
filename,
|
|
@@ -377,7 +411,9 @@ class ArtifactLocalSync {
|
|
|
377
411
|
field,
|
|
378
412
|
type: type,
|
|
379
413
|
originalContent: content,
|
|
380
|
-
isModified:
|
|
414
|
+
isModified: fileExists, // Mark as modified if we overwrote existing file
|
|
415
|
+
existedBefore: fileExists,
|
|
416
|
+
previousContent: existingContent
|
|
381
417
|
};
|
|
382
418
|
}
|
|
383
419
|
/**
|
|
@@ -544,29 +580,105 @@ snow-flow sync status ${widget.sys_id}
|
|
|
544
580
|
return issues;
|
|
545
581
|
}
|
|
546
582
|
/**
|
|
547
|
-
* Strip headers/footers we added -
|
|
583
|
+
* Strip headers/footers we added - FIXED to use actual fieldMapping wrappers
|
|
548
584
|
*/
|
|
549
585
|
stripAddedWrappers(content, type, fieldMapping) {
|
|
550
|
-
|
|
586
|
+
if (!fieldMapping || !fieldMapping.wrapperHeader || !fieldMapping.wrapperFooter) {
|
|
587
|
+
// No wrappers defined, just remove generic comments
|
|
588
|
+
return this.stripGenericComments(content, type);
|
|
589
|
+
}
|
|
590
|
+
let cleaned = content;
|
|
591
|
+
// Get the actual wrappers that were added (without placeholders)
|
|
592
|
+
const header = fieldMapping.wrapperHeader;
|
|
593
|
+
const footer = fieldMapping.wrapperFooter;
|
|
594
|
+
// CRITICAL FIX: Use the EXACT wrappers from fieldMapping
|
|
595
|
+
if (header && footer) {
|
|
596
|
+
// Create regex patterns from the actual wrappers
|
|
597
|
+
const headerPattern = this.escapeRegexAndCreatePattern(header);
|
|
598
|
+
const footerPattern = this.escapeRegexAndCreatePattern(footer);
|
|
599
|
+
// Remove footer first (from end)
|
|
600
|
+
if (footerPattern) {
|
|
601
|
+
cleaned = cleaned.replace(new RegExp(footerPattern + '$'), '');
|
|
602
|
+
}
|
|
603
|
+
// Remove header (from start)
|
|
604
|
+
if (headerPattern) {
|
|
605
|
+
cleaned = cleaned.replace(new RegExp('^' + headerPattern), '');
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
return cleaned.trim();
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Create regex pattern from wrapper text, handling placeholders
|
|
612
|
+
*/
|
|
613
|
+
escapeRegexAndCreatePattern(wrapper) {
|
|
614
|
+
// Replace placeholders with generic patterns
|
|
615
|
+
let pattern = wrapper
|
|
616
|
+
.replace(/\{name\}/g, '[^}]*') // Replace {name} with pattern to match any name
|
|
617
|
+
.replace(/\{[^}]+\}/g, '[^}]*'); // Replace any other {placeholder}
|
|
618
|
+
// Escape special regex characters
|
|
619
|
+
pattern = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
620
|
+
// Convert \n back to actual newlines in pattern
|
|
621
|
+
pattern = pattern.replace(/\\n/g, '\\s*\\n\\s*');
|
|
622
|
+
return pattern;
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Fallback: Strip generic comments when no fieldMapping available
|
|
626
|
+
*/
|
|
627
|
+
stripGenericComments(content, type) {
|
|
551
628
|
let cleaned = content;
|
|
552
629
|
if (type === 'js') {
|
|
553
|
-
// Remove
|
|
554
|
-
cleaned = cleaned.replace(
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
cleaned = cleaned.replace(
|
|
558
|
-
cleaned = cleaned.replace(/^function\(
|
|
630
|
+
// Remove generic comment blocks at start
|
|
631
|
+
cleaned = cleaned.replace(/^\/\*\*[\s\S]*?\*\/\s*\n?/, '');
|
|
632
|
+
// Remove simple function wrappers (conservative approach)
|
|
633
|
+
cleaned = cleaned.replace(/^\(function\(\) \{\s*\n?/, '');
|
|
634
|
+
cleaned = cleaned.replace(/\s*\n?\}\)\(\);\s*$/, '');
|
|
635
|
+
cleaned = cleaned.replace(/^function\(\s*/, 'function(');
|
|
559
636
|
}
|
|
560
637
|
else if (type === 'html') {
|
|
561
|
-
// Remove
|
|
562
|
-
cleaned = cleaned.replace(/^<!--
|
|
638
|
+
// Remove HTML comments
|
|
639
|
+
cleaned = cleaned.replace(/^<!--[\s\S]*?-->\s*\n?/, '');
|
|
563
640
|
}
|
|
564
641
|
else if (type === 'css') {
|
|
565
|
-
// Remove
|
|
566
|
-
cleaned = cleaned.replace(/^\/\*
|
|
642
|
+
// Remove CSS comments
|
|
643
|
+
cleaned = cleaned.replace(/^\/\*[\s\S]*?\*\/\s*\n?/, '');
|
|
567
644
|
}
|
|
568
645
|
return cleaned.trim();
|
|
569
646
|
}
|
|
647
|
+
/**
|
|
648
|
+
* INTELLIGENT WRAPPER DETECTION: Check if content actually needs wrappers
|
|
649
|
+
*/
|
|
650
|
+
needsWrappers(content, mapping) {
|
|
651
|
+
if (!content || !content.trim()) {
|
|
652
|
+
return false; // Empty content doesn't need wrappers
|
|
653
|
+
}
|
|
654
|
+
// Check for server script wrappers
|
|
655
|
+
if (mapping.wrapperHeader?.includes('(function()') && mapping.wrapperFooter?.includes('})()')) {
|
|
656
|
+
// Check if content already has function wrapper
|
|
657
|
+
const trimmed = content.trim();
|
|
658
|
+
if (trimmed.match(/^\(function\s*\([^)]*\)\s*\{[\s\S]*\}\s*\)\s*\([^)]*\)\s*;?$/)) {
|
|
659
|
+
return false; // Already has function wrapper
|
|
660
|
+
}
|
|
661
|
+
if (trimmed.match(/^\(function\(\)\s*\{[\s\S]*\}\)\(\)\s*;?$/)) {
|
|
662
|
+
return false; // Already has our specific wrapper
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
// Check for client script wrappers
|
|
666
|
+
if (mapping.wrapperHeader?.includes('function(') && mapping.wrapperFooter?.includes(')')) {
|
|
667
|
+
const trimmed = content.trim();
|
|
668
|
+
if (trimmed.match(/^function\s*\([^)]*\)\s*\{[\s\S]*\}$/)) {
|
|
669
|
+
return false; // Already is a function
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
// Check if content already has comments that look like ours
|
|
673
|
+
if (mapping.wrapperHeader?.includes('/**')) {
|
|
674
|
+
if (content.includes('* Server Script for Widget:') ||
|
|
675
|
+
content.includes('* Client Controller for Widget:') ||
|
|
676
|
+
content.includes('* ES5 ONLY -')) {
|
|
677
|
+
return false; // Already has our comments
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
return true; // Content needs wrappers
|
|
681
|
+
}
|
|
570
682
|
/**
|
|
571
683
|
* Sanitize filename for filesystem
|
|
572
684
|
*/
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "snow-flow",
|
|
3
|
-
"version": "3.5.
|
|
4
|
-
"description": "ServiceNow development framework with
|
|
3
|
+
"version": "3.5.15",
|
|
4
|
+
"description": "ServiceNow development framework with FIXED local artifact sync system. CRITICAL BUG RESOLVED: Fixed cumulative wrapper problems in snow_pull_artifact where dynamic registry wrappers didn't match hardcoded strip patterns, causing dubbele function wrappers. Now features intelligent wrapper detection and proper content handling. Enhanced 'snow-flow init' creates optimized settings.json with 18 MCP servers, extensive development permissions, intelligent hooks, and ServiceNow-specific commands. Features intelligent timeout configuration and comprehensive ServiceNow development workflow.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "commonjs",
|
|
7
7
|
"bin": {
|