aios-core 4.2.13 → 4.2.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.
- package/.aios-core/core/code-intel/helpers/dev-helper.js +206 -0
- package/.aios-core/core/registry/registry-schema.json +166 -166
- package/.aios-core/core/synapse/diagnostics/collectors/hook-collector.js +3 -3
- package/.aios-core/data/entity-registry.yaml +27 -0
- package/.aios-core/development/scripts/approval-workflow.js +642 -642
- package/.aios-core/development/scripts/backup-manager.js +606 -606
- package/.aios-core/development/scripts/branch-manager.js +389 -389
- package/.aios-core/development/scripts/code-quality-improver.js +1311 -1311
- package/.aios-core/development/scripts/commit-message-generator.js +849 -849
- package/.aios-core/development/scripts/conflict-resolver.js +674 -674
- package/.aios-core/development/scripts/dependency-analyzer.js +637 -637
- package/.aios-core/development/scripts/diff-generator.js +351 -351
- package/.aios-core/development/scripts/elicitation-engine.js +384 -384
- package/.aios-core/development/scripts/elicitation-session-manager.js +299 -299
- package/.aios-core/development/scripts/git-wrapper.js +461 -461
- package/.aios-core/development/scripts/manifest-preview.js +244 -244
- package/.aios-core/development/scripts/metrics-tracker.js +775 -775
- package/.aios-core/development/scripts/modification-validator.js +554 -554
- package/.aios-core/development/scripts/pattern-learner.js +1224 -1224
- package/.aios-core/development/scripts/performance-analyzer.js +757 -757
- package/.aios-core/development/scripts/refactoring-suggester.js +1138 -1138
- package/.aios-core/development/scripts/rollback-handler.js +530 -530
- package/.aios-core/development/scripts/security-checker.js +358 -358
- package/.aios-core/development/scripts/template-engine.js +239 -239
- package/.aios-core/development/scripts/template-validator.js +278 -278
- package/.aios-core/development/scripts/test-generator.js +843 -843
- package/.aios-core/development/scripts/transaction-manager.js +589 -589
- package/.aios-core/development/scripts/usage-tracker.js +673 -673
- package/.aios-core/development/scripts/validate-filenames.js +226 -226
- package/.aios-core/development/scripts/version-tracker.js +526 -526
- package/.aios-core/development/scripts/yaml-validator.js +396 -396
- package/.aios-core/development/tasks/build-autonomous.md +10 -4
- package/.aios-core/development/tasks/create-service.md +23 -0
- package/.aios-core/development/tasks/dev-develop-story.md +12 -6
- package/.aios-core/development/tasks/dev-suggest-refactoring.md +7 -1
- package/.aios-core/development/tasks/publish-npm.md +3 -3
- package/.aios-core/hooks/unified/README.md +1 -1
- package/.aios-core/install-manifest.yaml +65 -61
- package/.aios-core/manifests/schema/manifest-schema.json +190 -190
- package/.aios-core/product/templates/component-react-tmpl.tsx +98 -98
- package/.aios-core/product/templates/engine/schemas/adr.schema.json +102 -102
- package/.aios-core/product/templates/engine/schemas/dbdr.schema.json +205 -205
- package/.aios-core/product/templates/engine/schemas/epic.schema.json +175 -175
- package/.aios-core/product/templates/engine/schemas/pmdr.schema.json +175 -175
- package/.aios-core/product/templates/engine/schemas/prd-v2.schema.json +300 -300
- package/.aios-core/product/templates/engine/schemas/prd.schema.json +152 -152
- package/.aios-core/product/templates/engine/schemas/story.schema.json +222 -222
- package/.aios-core/product/templates/engine/schemas/task.schema.json +154 -154
- package/.aios-core/product/templates/eslintrc-security.json +32 -32
- package/.aios-core/product/templates/github-actions-cd.yml +212 -212
- package/.aios-core/product/templates/github-actions-ci.yml +172 -172
- package/.aios-core/product/templates/shock-report-tmpl.html +502 -502
- package/.aios-core/product/templates/token-exports-css-tmpl.css +240 -240
- package/.aios-core/quality/schemas/quality-metrics.schema.json +233 -233
- package/.aios-core/scripts/migrate-framework-docs.sh +300 -300
- package/README.en.md +747 -0
- package/README.md +4 -2
- package/bin/aios.js +7 -4
- package/package.json +1 -1
- package/packages/aios-pro-cli/src/recover.js +1 -1
- package/packages/installer/src/wizard/ide-config-generator.js +6 -6
- package/packages/installer/src/wizard/pro-setup.js +3 -3
- package/pro/license/degradation.js +220 -220
- package/pro/license/errors.js +450 -450
- package/pro/license/feature-gate.js +354 -354
- package/pro/license/index.js +181 -181
- package/pro/license/license-cache.js +523 -523
- package/pro/license/license-crypto.js +303 -303
- package/scripts/package-synapse.js +5 -5
- package/scripts/validate-package-completeness.js +3 -3
- package/.aios-core/.session/current-session.json +0 -14
- package/.aios-core/data/registry-update-log.jsonl +0 -191
- package/.aios-core/docs/SHARD-TRANSLATION-GUIDE.md +0 -335
- package/.aios-core/docs/component-creation-guide.md +0 -458
- package/.aios-core/docs/session-update-pattern.md +0 -307
- package/.aios-core/docs/standards/AIOS-FRAMEWORK-MASTER.md +0 -1963
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-SUMMARY.md +0 -1190
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1.md +0 -439
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO.md +0 -5398
- package/.aios-core/docs/standards/V3-ARCHITECTURAL-DECISIONS.md +0 -523
- package/.aios-core/docs/template-syntax.md +0 -267
- package/.aios-core/docs/troubleshooting-guide.md +0 -625
- package/.aios-core/infrastructure/tests/utilities-audit-results.json +0 -501
- package/.aios-core/manifests/agents.csv +0 -29
- package/.aios-core/manifests/tasks.csv +0 -198
- package/.aios-core/manifests/workers.csv +0 -204
- package/.claude/rules/agent-authority.md +0 -105
- package/.claude/rules/coderabbit-integration.md +0 -93
- package/.claude/rules/ids-principles.md +0 -112
- package/.claude/rules/story-lifecycle.md +0 -139
- package/.claude/rules/workflow-execution.md +0 -150
- package/scripts/glue/README.md +0 -355
- package/scripts/glue/compose-agent-prompt.cjs +0 -362
- /package/.claude/hooks/{precompact-session-digest.js → precompact-session-digest.cjs} +0 -0
- /package/.claude/hooks/{synapse-engine.js → synapse-engine.cjs} +0 -0
|
@@ -1,240 +1,240 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Template Engine for AIOS-FULLSTACK
|
|
3
|
-
* Handles variable substitution, conditionals, and loops for component generation
|
|
4
|
-
* @module template-engine
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const fs = require('fs-extra');
|
|
8
|
-
const _path = require('path');
|
|
9
|
-
|
|
10
|
-
class TemplateEngine {
|
|
11
|
-
constructor() {
|
|
12
|
-
this.variablePattern = /\{\{([^}]+)\}\}/g;
|
|
13
|
-
this.conditionalPattern = /\{\{#IF_([^}]+)\}\}([\s\S]*?)\{\{\/IF_\1\}\}/g;
|
|
14
|
-
this.loopPattern = /\{\{#EACH_([^}]+)\}\}([\s\S]*?)\{\{\/EACH_\1\}\}/g;
|
|
15
|
-
this.escapePattern = /\\\{\{([^}]+)\}\}/g;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Process a template string with given variables
|
|
20
|
-
* @param {string} template - The template string
|
|
21
|
-
* @param {Object} variables - Variables to substitute
|
|
22
|
-
* @returns {string} Processed template
|
|
23
|
-
*/
|
|
24
|
-
process(template, variables = {}) {
|
|
25
|
-
// First, handle escaped braces
|
|
26
|
-
let processed = template.replace(this.escapePattern, '{{$1}}');
|
|
27
|
-
|
|
28
|
-
// Process loops
|
|
29
|
-
processed = this.processLoops(processed, variables);
|
|
30
|
-
|
|
31
|
-
// Process conditionals
|
|
32
|
-
processed = this.processConditionals(processed, variables);
|
|
33
|
-
|
|
34
|
-
// Process simple variables
|
|
35
|
-
processed = this.processVariables(processed, variables);
|
|
36
|
-
|
|
37
|
-
// Restore escaped braces
|
|
38
|
-
processed = processed.replace(/\{\{ESCAPED_BRACE_(LEFT|RIGHT)\}\}/g, (match, side) =>
|
|
39
|
-
side === 'LEFT' ? '{{' : '}}'
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
return processed;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Process loop constructs in template
|
|
47
|
-
* @private
|
|
48
|
-
* @param {string} template - Template string
|
|
49
|
-
* @param {Object} variables - Variables object
|
|
50
|
-
* @returns {string} Processed template
|
|
51
|
-
*/
|
|
52
|
-
processLoops(template, variables) {
|
|
53
|
-
return template.replace(this.loopPattern, (match, loopVar, content) => {
|
|
54
|
-
const items = this.resolveVariable(loopVar, variables);
|
|
55
|
-
|
|
56
|
-
// Handle non-array values gracefully
|
|
57
|
-
if (!Array.isArray(items)) {
|
|
58
|
-
console.warn(`[TemplateEngine] Expected array for loop variable '${loopVar}', got ${typeof items}`);
|
|
59
|
-
return '';
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return items.map((item, index) => {
|
|
63
|
-
// Create proper loop context with item and metadata
|
|
64
|
-
const loopVars = {
|
|
65
|
-
...variables,
|
|
66
|
-
ITEM: item,
|
|
67
|
-
INDEX: index,
|
|
68
|
-
FIRST: index === 0,
|
|
69
|
-
LAST: index === items.length - 1,
|
|
70
|
-
[loopVar.replace('_', '')]: item
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
// Process nested loops and conditionals
|
|
74
|
-
let processedContent = this.processLoops(content, loopVars);
|
|
75
|
-
processedContent = this.processConditionals(processedContent, loopVars);
|
|
76
|
-
processedContent = this.processVariables(processedContent, loopVars);
|
|
77
|
-
|
|
78
|
-
return processedContent;
|
|
79
|
-
}).join('');
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Process conditional constructs in template
|
|
85
|
-
* @private
|
|
86
|
-
*/
|
|
87
|
-
processConditionals(template, variables) {
|
|
88
|
-
return template.replace(this.conditionalPattern, (match, condition, content) => {
|
|
89
|
-
const value = this.resolveVariable(condition, variables);
|
|
90
|
-
|
|
91
|
-
// Check for truthy value
|
|
92
|
-
if (value && (Array.isArray(value) ? value.length > 0 : true)) {
|
|
93
|
-
return content;
|
|
94
|
-
}
|
|
95
|
-
return '';
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Process simple variable substitutions
|
|
101
|
-
* @private
|
|
102
|
-
*/
|
|
103
|
-
processVariables(template, variables) {
|
|
104
|
-
return template.replace(this.variablePattern, (match, varName) => {
|
|
105
|
-
const value = this.resolveVariable(varName.trim(), variables);
|
|
106
|
-
return value !== undefined ? String(value) : match;
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Resolve a variable name to its value
|
|
112
|
-
* @private
|
|
113
|
-
*/
|
|
114
|
-
resolveVariable(varName, variables) {
|
|
115
|
-
// Handle nested paths like "user.name"
|
|
116
|
-
const parts = varName.split('.');
|
|
117
|
-
let value = variables;
|
|
118
|
-
|
|
119
|
-
for (const part of parts) {
|
|
120
|
-
if (value && typeof value === 'object') {
|
|
121
|
-
value = value[part];
|
|
122
|
-
} else {
|
|
123
|
-
return undefined;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return value;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Validate that a template has all required placeholders
|
|
132
|
-
* @param {string} template - Template to validate
|
|
133
|
-
* @param {string[]} requiredVars - List of required variable names
|
|
134
|
-
* @returns {Object} Validation result with {valid: boolean, missing: string[]}
|
|
135
|
-
*/
|
|
136
|
-
validateTemplate(template, requiredVars = []) {
|
|
137
|
-
const foundVars = new Set();
|
|
138
|
-
const missing = [];
|
|
139
|
-
|
|
140
|
-
// Extract all variable names from template
|
|
141
|
-
let match;
|
|
142
|
-
const allPatterns = [
|
|
143
|
-
this.variablePattern,
|
|
144
|
-
/\{\{#IF_([^}]+)\}\}/g,
|
|
145
|
-
/\{\{#EACH_([^}]+)\}\}/g
|
|
146
|
-
];
|
|
147
|
-
|
|
148
|
-
for (const pattern of allPatterns) {
|
|
149
|
-
pattern.lastIndex = 0; // Reset regex
|
|
150
|
-
while ((match = pattern.exec(template)) !== null) {
|
|
151
|
-
foundVars.add(match[1].trim());
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Check for missing required variables
|
|
156
|
-
for (const reqVar of requiredVars) {
|
|
157
|
-
if (!foundVars.has(reqVar)) {
|
|
158
|
-
missing.push(reqVar);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return {
|
|
163
|
-
valid: missing.length === 0,
|
|
164
|
-
missing,
|
|
165
|
-
found: Array.from(foundVars)
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Load and process a template file
|
|
171
|
-
* @param {string} templatePath - Path to template file
|
|
172
|
-
* @param {Object} variables - Variables to substitute
|
|
173
|
-
* @returns {Promise<string>} Processed template
|
|
174
|
-
*/
|
|
175
|
-
async loadAndProcess(templatePath, variables = {}) {
|
|
176
|
-
const template = await fs.readFile(templatePath, 'utf8');
|
|
177
|
-
return this.process(template, variables);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Escape special characters in user input to prevent injection
|
|
182
|
-
* @param {string} input - User input to escape
|
|
183
|
-
* @returns {string} Escaped input
|
|
184
|
-
*/
|
|
185
|
-
escapeInput(input) {
|
|
186
|
-
if (typeof input !== 'string') return input;
|
|
187
|
-
|
|
188
|
-
// Escape template syntax
|
|
189
|
-
return input
|
|
190
|
-
.replace(/\{\{/g, '\\{{')
|
|
191
|
-
.replace(/\}\}/g, '\\}}')
|
|
192
|
-
.replace(/</g, '<')
|
|
193
|
-
.replace(/>/g, '>')
|
|
194
|
-
.replace(/"/g, '"')
|
|
195
|
-
.replace(/'/g, ''');
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Get all available variables from a template
|
|
200
|
-
* @param {string} template - Template to analyze
|
|
201
|
-
* @returns {Object} Object with variables categorized by type
|
|
202
|
-
*/
|
|
203
|
-
getTemplateVariables(template) {
|
|
204
|
-
const variables = {
|
|
205
|
-
simple: [],
|
|
206
|
-
conditionals: [],
|
|
207
|
-
loops: []
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
// Extract simple variables
|
|
211
|
-
let match;
|
|
212
|
-
this.variablePattern.lastIndex = 0;
|
|
213
|
-
while ((match = this.variablePattern.exec(template)) !== null) {
|
|
214
|
-
if (!match[1].startsWith('#') && !match[1].startsWith('/')) {
|
|
215
|
-
variables.simple.push(match[1].trim());
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Extract conditionals
|
|
220
|
-
const conditionalStartPattern = /\{\{#IF_([^}]+)\}\}/g;
|
|
221
|
-
while ((match = conditionalStartPattern.exec(template)) !== null) {
|
|
222
|
-
variables.conditionals.push(match[1].trim());
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Extract loops
|
|
226
|
-
const loopStartPattern = /\{\{#EACH_([^}]+)\}\}/g;
|
|
227
|
-
while ((match = loopStartPattern.exec(template)) !== null) {
|
|
228
|
-
variables.loops.push(match[1].trim());
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Remove duplicates
|
|
232
|
-
for (const key in variables) {
|
|
233
|
-
variables[key] = [...new Set(variables[key])];
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
return variables;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Template Engine for AIOS-FULLSTACK
|
|
3
|
+
* Handles variable substitution, conditionals, and loops for component generation
|
|
4
|
+
* @module template-engine
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs-extra');
|
|
8
|
+
const _path = require('path');
|
|
9
|
+
|
|
10
|
+
class TemplateEngine {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.variablePattern = /\{\{([^}]+)\}\}/g;
|
|
13
|
+
this.conditionalPattern = /\{\{#IF_([^}]+)\}\}([\s\S]*?)\{\{\/IF_\1\}\}/g;
|
|
14
|
+
this.loopPattern = /\{\{#EACH_([^}]+)\}\}([\s\S]*?)\{\{\/EACH_\1\}\}/g;
|
|
15
|
+
this.escapePattern = /\\\{\{([^}]+)\}\}/g;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Process a template string with given variables
|
|
20
|
+
* @param {string} template - The template string
|
|
21
|
+
* @param {Object} variables - Variables to substitute
|
|
22
|
+
* @returns {string} Processed template
|
|
23
|
+
*/
|
|
24
|
+
process(template, variables = {}) {
|
|
25
|
+
// First, handle escaped braces
|
|
26
|
+
let processed = template.replace(this.escapePattern, '{{$1}}');
|
|
27
|
+
|
|
28
|
+
// Process loops
|
|
29
|
+
processed = this.processLoops(processed, variables);
|
|
30
|
+
|
|
31
|
+
// Process conditionals
|
|
32
|
+
processed = this.processConditionals(processed, variables);
|
|
33
|
+
|
|
34
|
+
// Process simple variables
|
|
35
|
+
processed = this.processVariables(processed, variables);
|
|
36
|
+
|
|
37
|
+
// Restore escaped braces
|
|
38
|
+
processed = processed.replace(/\{\{ESCAPED_BRACE_(LEFT|RIGHT)\}\}/g, (match, side) =>
|
|
39
|
+
side === 'LEFT' ? '{{' : '}}'
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
return processed;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Process loop constructs in template
|
|
47
|
+
* @private
|
|
48
|
+
* @param {string} template - Template string
|
|
49
|
+
* @param {Object} variables - Variables object
|
|
50
|
+
* @returns {string} Processed template
|
|
51
|
+
*/
|
|
52
|
+
processLoops(template, variables) {
|
|
53
|
+
return template.replace(this.loopPattern, (match, loopVar, content) => {
|
|
54
|
+
const items = this.resolveVariable(loopVar, variables);
|
|
55
|
+
|
|
56
|
+
// Handle non-array values gracefully
|
|
57
|
+
if (!Array.isArray(items)) {
|
|
58
|
+
console.warn(`[TemplateEngine] Expected array for loop variable '${loopVar}', got ${typeof items}`);
|
|
59
|
+
return '';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return items.map((item, index) => {
|
|
63
|
+
// Create proper loop context with item and metadata
|
|
64
|
+
const loopVars = {
|
|
65
|
+
...variables,
|
|
66
|
+
ITEM: item,
|
|
67
|
+
INDEX: index,
|
|
68
|
+
FIRST: index === 0,
|
|
69
|
+
LAST: index === items.length - 1,
|
|
70
|
+
[loopVar.replace('_', '')]: item
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Process nested loops and conditionals
|
|
74
|
+
let processedContent = this.processLoops(content, loopVars);
|
|
75
|
+
processedContent = this.processConditionals(processedContent, loopVars);
|
|
76
|
+
processedContent = this.processVariables(processedContent, loopVars);
|
|
77
|
+
|
|
78
|
+
return processedContent;
|
|
79
|
+
}).join('');
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Process conditional constructs in template
|
|
85
|
+
* @private
|
|
86
|
+
*/
|
|
87
|
+
processConditionals(template, variables) {
|
|
88
|
+
return template.replace(this.conditionalPattern, (match, condition, content) => {
|
|
89
|
+
const value = this.resolveVariable(condition, variables);
|
|
90
|
+
|
|
91
|
+
// Check for truthy value
|
|
92
|
+
if (value && (Array.isArray(value) ? value.length > 0 : true)) {
|
|
93
|
+
return content;
|
|
94
|
+
}
|
|
95
|
+
return '';
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Process simple variable substitutions
|
|
101
|
+
* @private
|
|
102
|
+
*/
|
|
103
|
+
processVariables(template, variables) {
|
|
104
|
+
return template.replace(this.variablePattern, (match, varName) => {
|
|
105
|
+
const value = this.resolveVariable(varName.trim(), variables);
|
|
106
|
+
return value !== undefined ? String(value) : match;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Resolve a variable name to its value
|
|
112
|
+
* @private
|
|
113
|
+
*/
|
|
114
|
+
resolveVariable(varName, variables) {
|
|
115
|
+
// Handle nested paths like "user.name"
|
|
116
|
+
const parts = varName.split('.');
|
|
117
|
+
let value = variables;
|
|
118
|
+
|
|
119
|
+
for (const part of parts) {
|
|
120
|
+
if (value && typeof value === 'object') {
|
|
121
|
+
value = value[part];
|
|
122
|
+
} else {
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Validate that a template has all required placeholders
|
|
132
|
+
* @param {string} template - Template to validate
|
|
133
|
+
* @param {string[]} requiredVars - List of required variable names
|
|
134
|
+
* @returns {Object} Validation result with {valid: boolean, missing: string[]}
|
|
135
|
+
*/
|
|
136
|
+
validateTemplate(template, requiredVars = []) {
|
|
137
|
+
const foundVars = new Set();
|
|
138
|
+
const missing = [];
|
|
139
|
+
|
|
140
|
+
// Extract all variable names from template
|
|
141
|
+
let match;
|
|
142
|
+
const allPatterns = [
|
|
143
|
+
this.variablePattern,
|
|
144
|
+
/\{\{#IF_([^}]+)\}\}/g,
|
|
145
|
+
/\{\{#EACH_([^}]+)\}\}/g
|
|
146
|
+
];
|
|
147
|
+
|
|
148
|
+
for (const pattern of allPatterns) {
|
|
149
|
+
pattern.lastIndex = 0; // Reset regex
|
|
150
|
+
while ((match = pattern.exec(template)) !== null) {
|
|
151
|
+
foundVars.add(match[1].trim());
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Check for missing required variables
|
|
156
|
+
for (const reqVar of requiredVars) {
|
|
157
|
+
if (!foundVars.has(reqVar)) {
|
|
158
|
+
missing.push(reqVar);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
valid: missing.length === 0,
|
|
164
|
+
missing,
|
|
165
|
+
found: Array.from(foundVars)
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Load and process a template file
|
|
171
|
+
* @param {string} templatePath - Path to template file
|
|
172
|
+
* @param {Object} variables - Variables to substitute
|
|
173
|
+
* @returns {Promise<string>} Processed template
|
|
174
|
+
*/
|
|
175
|
+
async loadAndProcess(templatePath, variables = {}) {
|
|
176
|
+
const template = await fs.readFile(templatePath, 'utf8');
|
|
177
|
+
return this.process(template, variables);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Escape special characters in user input to prevent injection
|
|
182
|
+
* @param {string} input - User input to escape
|
|
183
|
+
* @returns {string} Escaped input
|
|
184
|
+
*/
|
|
185
|
+
escapeInput(input) {
|
|
186
|
+
if (typeof input !== 'string') return input;
|
|
187
|
+
|
|
188
|
+
// Escape template syntax
|
|
189
|
+
return input
|
|
190
|
+
.replace(/\{\{/g, '\\{{')
|
|
191
|
+
.replace(/\}\}/g, '\\}}')
|
|
192
|
+
.replace(/</g, '<')
|
|
193
|
+
.replace(/>/g, '>')
|
|
194
|
+
.replace(/"/g, '"')
|
|
195
|
+
.replace(/'/g, ''');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Get all available variables from a template
|
|
200
|
+
* @param {string} template - Template to analyze
|
|
201
|
+
* @returns {Object} Object with variables categorized by type
|
|
202
|
+
*/
|
|
203
|
+
getTemplateVariables(template) {
|
|
204
|
+
const variables = {
|
|
205
|
+
simple: [],
|
|
206
|
+
conditionals: [],
|
|
207
|
+
loops: []
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// Extract simple variables
|
|
211
|
+
let match;
|
|
212
|
+
this.variablePattern.lastIndex = 0;
|
|
213
|
+
while ((match = this.variablePattern.exec(template)) !== null) {
|
|
214
|
+
if (!match[1].startsWith('#') && !match[1].startsWith('/')) {
|
|
215
|
+
variables.simple.push(match[1].trim());
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Extract conditionals
|
|
220
|
+
const conditionalStartPattern = /\{\{#IF_([^}]+)\}\}/g;
|
|
221
|
+
while ((match = conditionalStartPattern.exec(template)) !== null) {
|
|
222
|
+
variables.conditionals.push(match[1].trim());
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Extract loops
|
|
226
|
+
const loopStartPattern = /\{\{#EACH_([^}]+)\}\}/g;
|
|
227
|
+
while ((match = loopStartPattern.exec(template)) !== null) {
|
|
228
|
+
variables.loops.push(match[1].trim());
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Remove duplicates
|
|
232
|
+
for (const key in variables) {
|
|
233
|
+
variables[key] = [...new Set(variables[key])];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return variables;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
240
|
module.exports = TemplateEngine;
|