@63klabs/cache-data 1.3.4 → 1.3.6
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/AI_CONTEXT.md +863 -0
- package/CHANGELOG.md +44 -1
- package/CONTRIBUTING.md +161 -0
- package/README.md +139 -27
- package/package.json +9 -5
- package/scripts/README.md +175 -0
- package/scripts/audit-documentation.mjs +856 -0
- package/scripts/review-documentation-files.mjs +406 -0
- package/scripts/setup-dev-environment.sh +59 -0
- package/scripts/verify-example-files.mjs +194 -0
- package/src/lib/dao-cache.js +1286 -288
- package/src/lib/dao-endpoint.js +181 -42
- package/src/lib/tools/AWS.classes.js +82 -0
- package/src/lib/tools/CachedParametersSecrets.classes.js +98 -7
- package/src/lib/tools/ClientRequest.class.js +43 -10
- package/src/lib/tools/Connections.classes.js +148 -13
- package/src/lib/tools/DebugAndLog.class.js +244 -75
- package/src/lib/tools/ImmutableObject.class.js +44 -2
- package/src/lib/tools/RequestInfo.class.js +38 -0
- package/src/lib/tools/Response.class.js +245 -81
- package/src/lib/tools/ResponseDataModel.class.js +123 -47
- package/src/lib/tools/Timer.class.js +138 -26
- package/src/lib/tools/index.js +89 -2
- package/src/lib/tools/utils.js +40 -4
- package/src/lib/utils/InMemoryCache.js +221 -0
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Documentation Files Review Script
|
|
5
|
+
*
|
|
6
|
+
* Reviews all documentation files in docs/ directory to:
|
|
7
|
+
* - List all files
|
|
8
|
+
* - Check for broken links (internal file references)
|
|
9
|
+
* - Identify outdated content or examples
|
|
10
|
+
* - Note missing documentation for features
|
|
11
|
+
*
|
|
12
|
+
* Requirements: 3.1, 3.4, 4.1
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import fs from 'fs';
|
|
16
|
+
import path from 'path';
|
|
17
|
+
import { fileURLToPath } from 'url';
|
|
18
|
+
|
|
19
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
20
|
+
const __dirname = path.dirname(__filename);
|
|
21
|
+
|
|
22
|
+
// Configuration
|
|
23
|
+
const DOCS_DIR = path.join(__dirname, '..', 'docs');
|
|
24
|
+
const ROOT_DIR = path.join(__dirname, '..');
|
|
25
|
+
const OUTPUT_FILE = path.join(__dirname, '..', '.kiro', 'specs', '1-3-6-documentation-enhancement', 'docs-review-report.json');
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Recursively get all markdown files in a directory
|
|
29
|
+
* @param {string} dir Directory to scan
|
|
30
|
+
* @param {Array<string>} fileList Accumulated file list
|
|
31
|
+
* @returns {Array<string>} List of file paths
|
|
32
|
+
*/
|
|
33
|
+
function getMarkdownFiles(dir, fileList = []) {
|
|
34
|
+
try {
|
|
35
|
+
const files = fs.readdirSync(dir);
|
|
36
|
+
|
|
37
|
+
files.forEach(file => {
|
|
38
|
+
const filePath = path.join(dir, file);
|
|
39
|
+
const stat = fs.statSync(filePath);
|
|
40
|
+
|
|
41
|
+
if (stat.isDirectory()) {
|
|
42
|
+
getMarkdownFiles(filePath, fileList);
|
|
43
|
+
} else if (file.endsWith('.md')) {
|
|
44
|
+
fileList.push(filePath);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.error(`Error reading directory ${dir}:`, error.message);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return fileList;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Extract links from markdown content
|
|
56
|
+
* @param {string} content Markdown content
|
|
57
|
+
* @returns {Array<Object>} Array of link objects with text and url
|
|
58
|
+
*/
|
|
59
|
+
function extractLinks(content) {
|
|
60
|
+
const links = [];
|
|
61
|
+
|
|
62
|
+
// Match markdown links: [text](url)
|
|
63
|
+
const markdownLinkPattern = /\[([^\]]+)\]\(([^)]+)\)/g;
|
|
64
|
+
let match;
|
|
65
|
+
|
|
66
|
+
while ((match = markdownLinkPattern.exec(content)) !== null) {
|
|
67
|
+
links.push({
|
|
68
|
+
text: match[1],
|
|
69
|
+
url: match[2],
|
|
70
|
+
type: 'markdown'
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Match HTML links: <a href="url">text</a>
|
|
75
|
+
const htmlLinkPattern = /<a\s+(?:[^>]*?\s+)?href="([^"]*)"[^>]*>(.*?)<\/a>/gi;
|
|
76
|
+
while ((match = htmlLinkPattern.exec(content)) !== null) {
|
|
77
|
+
links.push({
|
|
78
|
+
text: match[2],
|
|
79
|
+
url: match[1],
|
|
80
|
+
type: 'html'
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return links;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Check if a link is valid
|
|
89
|
+
* @param {string} url URL to check
|
|
90
|
+
* @param {string} sourceFile File containing the link
|
|
91
|
+
* @returns {Object} Validation result
|
|
92
|
+
*/
|
|
93
|
+
function validateLink(url, sourceFile) {
|
|
94
|
+
// Skip external URLs (we'll just note them, not validate)
|
|
95
|
+
if (url.startsWith('http://') || url.startsWith('https://')) {
|
|
96
|
+
return {
|
|
97
|
+
valid: true,
|
|
98
|
+
type: 'external',
|
|
99
|
+
url
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Skip anchors within the same page
|
|
104
|
+
if (url.startsWith('#')) {
|
|
105
|
+
return {
|
|
106
|
+
valid: true,
|
|
107
|
+
type: 'anchor',
|
|
108
|
+
url,
|
|
109
|
+
note: 'Anchor links not validated'
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Handle relative paths
|
|
114
|
+
let targetPath = url;
|
|
115
|
+
|
|
116
|
+
// Remove anchor from URL
|
|
117
|
+
const anchorIndex = targetPath.indexOf('#');
|
|
118
|
+
if (anchorIndex !== -1) {
|
|
119
|
+
targetPath = targetPath.substring(0, anchorIndex);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Resolve relative to source file
|
|
123
|
+
const sourceDir = path.dirname(sourceFile);
|
|
124
|
+
const resolvedPath = path.resolve(sourceDir, targetPath);
|
|
125
|
+
|
|
126
|
+
// Check if file exists
|
|
127
|
+
const exists = fs.existsSync(resolvedPath);
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
valid: exists,
|
|
131
|
+
type: 'internal',
|
|
132
|
+
url,
|
|
133
|
+
resolvedPath,
|
|
134
|
+
exists
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Check for outdated patterns in content
|
|
140
|
+
* @param {string} content File content
|
|
141
|
+
* @param {string} filePath File path
|
|
142
|
+
* @returns {Array<Object>} Array of potential issues
|
|
143
|
+
*/
|
|
144
|
+
function checkForOutdatedContent(content, filePath) {
|
|
145
|
+
const issues = [];
|
|
146
|
+
|
|
147
|
+
// Check for old Node.js version references
|
|
148
|
+
const nodeVersionPattern = /node\.?js\s+(?:version\s+)?(\d+)\.(\d+)/gi;
|
|
149
|
+
let match;
|
|
150
|
+
while ((match = nodeVersionPattern.exec(content)) !== null) {
|
|
151
|
+
const major = parseInt(match[1]);
|
|
152
|
+
if (major < 20) {
|
|
153
|
+
issues.push({
|
|
154
|
+
type: 'outdated-node-version',
|
|
155
|
+
line: content.substring(0, match.index).split('\n').length,
|
|
156
|
+
text: match[0],
|
|
157
|
+
note: `References Node.js ${major}.x, but package.json requires >=20.0.0`
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Check for deprecated AWS SDK v2 references
|
|
163
|
+
if (content.includes('aws-sdk') && !content.includes('@aws-sdk')) {
|
|
164
|
+
const lines = content.split('\n');
|
|
165
|
+
lines.forEach((line, index) => {
|
|
166
|
+
if (line.includes('aws-sdk') && !line.includes('@aws-sdk')) {
|
|
167
|
+
issues.push({
|
|
168
|
+
type: 'deprecated-aws-sdk',
|
|
169
|
+
line: index + 1,
|
|
170
|
+
text: line.trim(),
|
|
171
|
+
note: 'References old aws-sdk package instead of @aws-sdk/*'
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Check for TODO or FIXME comments
|
|
178
|
+
const todoPattern = /(TODO|FIXME|XXX|HACK):\s*(.+)/gi;
|
|
179
|
+
while ((match = todoPattern.exec(content)) !== null) {
|
|
180
|
+
issues.push({
|
|
181
|
+
type: 'todo-comment',
|
|
182
|
+
line: content.substring(0, match.index).split('\n').length,
|
|
183
|
+
text: match[0],
|
|
184
|
+
note: 'Documentation contains TODO/FIXME comment'
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Check for broken code examples (missing closing backticks)
|
|
189
|
+
const codeBlockStarts = (content.match(/```/g) || []).length;
|
|
190
|
+
if (codeBlockStarts % 2 !== 0) {
|
|
191
|
+
issues.push({
|
|
192
|
+
type: 'malformed-code-block',
|
|
193
|
+
line: null,
|
|
194
|
+
text: null,
|
|
195
|
+
note: 'Unmatched code block delimiters (```)'
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return issues;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Analyze a single documentation file
|
|
204
|
+
* @param {string} filePath Path to documentation file
|
|
205
|
+
* @returns {Object} Analysis results
|
|
206
|
+
*/
|
|
207
|
+
function analyzeDocFile(filePath) {
|
|
208
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
209
|
+
const relativePath = path.relative(ROOT_DIR, filePath);
|
|
210
|
+
|
|
211
|
+
// Extract links
|
|
212
|
+
const links = extractLinks(content);
|
|
213
|
+
|
|
214
|
+
// Validate links
|
|
215
|
+
const brokenLinks = [];
|
|
216
|
+
const externalLinks = [];
|
|
217
|
+
const internalLinks = [];
|
|
218
|
+
|
|
219
|
+
links.forEach(link => {
|
|
220
|
+
const validation = validateLink(link.url, filePath);
|
|
221
|
+
|
|
222
|
+
if (validation.type === 'external') {
|
|
223
|
+
externalLinks.push({
|
|
224
|
+
text: link.text,
|
|
225
|
+
url: link.url
|
|
226
|
+
});
|
|
227
|
+
} else if (validation.type === 'internal') {
|
|
228
|
+
internalLinks.push({
|
|
229
|
+
text: link.text,
|
|
230
|
+
url: link.url,
|
|
231
|
+
valid: validation.valid
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
if (!validation.valid) {
|
|
235
|
+
brokenLinks.push({
|
|
236
|
+
text: link.text,
|
|
237
|
+
url: link.url,
|
|
238
|
+
resolvedPath: validation.resolvedPath
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// Check for outdated content
|
|
245
|
+
const outdatedIssues = checkForOutdatedContent(content, filePath);
|
|
246
|
+
|
|
247
|
+
// Get file stats
|
|
248
|
+
const stats = fs.statSync(filePath);
|
|
249
|
+
const lineCount = content.split('\n').length;
|
|
250
|
+
const wordCount = content.split(/\s+/).length;
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
filePath: relativePath,
|
|
254
|
+
stats: {
|
|
255
|
+
size: stats.size,
|
|
256
|
+
lastModified: stats.mtime.toISOString(),
|
|
257
|
+
lineCount,
|
|
258
|
+
wordCount
|
|
259
|
+
},
|
|
260
|
+
links: {
|
|
261
|
+
total: links.length,
|
|
262
|
+
external: externalLinks.length,
|
|
263
|
+
internal: internalLinks.length,
|
|
264
|
+
broken: brokenLinks.length
|
|
265
|
+
},
|
|
266
|
+
brokenLinks,
|
|
267
|
+
externalLinks,
|
|
268
|
+
outdatedIssues,
|
|
269
|
+
needsReview: brokenLinks.length > 0 || outdatedIssues.length > 0
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Check for missing documentation based on source code
|
|
275
|
+
* @returns {Array<Object>} Missing documentation items
|
|
276
|
+
*/
|
|
277
|
+
function checkMissingDocumentation() {
|
|
278
|
+
const missing = [];
|
|
279
|
+
|
|
280
|
+
// Check if main features are documented
|
|
281
|
+
const expectedDocs = [
|
|
282
|
+
{ path: 'docs/features/cache/README.md', feature: 'Cache Module' },
|
|
283
|
+
{ path: 'docs/features/endpoint/README.md', feature: 'Endpoint Module' },
|
|
284
|
+
{ path: 'docs/features/tools/README.md', feature: 'Tools Module' },
|
|
285
|
+
{ path: 'docs/00-quick-start-implementation/README.md', feature: 'Quick Start Guide' },
|
|
286
|
+
{ path: 'docs/01-advanced-implementation-for-web-service/README.md', feature: 'Advanced Implementation Guide' },
|
|
287
|
+
{ path: 'docs/00-example-implementation/README.md', feature: 'Example Implementation' },
|
|
288
|
+
{ path: 'docs/lambda-optimization/README.md', feature: 'Lambda Optimization Guide' },
|
|
289
|
+
{ path: 'docs/technical/in-memory-cache.md', feature: 'In-Memory Cache Technical Docs' }
|
|
290
|
+
];
|
|
291
|
+
|
|
292
|
+
expectedDocs.forEach(doc => {
|
|
293
|
+
const fullPath = path.join(ROOT_DIR, doc.path);
|
|
294
|
+
if (!fs.existsSync(fullPath)) {
|
|
295
|
+
missing.push({
|
|
296
|
+
path: doc.path,
|
|
297
|
+
feature: doc.feature,
|
|
298
|
+
status: 'missing'
|
|
299
|
+
});
|
|
300
|
+
} else {
|
|
301
|
+
// Check if file is empty or very small
|
|
302
|
+
const stats = fs.statSync(fullPath);
|
|
303
|
+
if (stats.size < 100) {
|
|
304
|
+
missing.push({
|
|
305
|
+
path: doc.path,
|
|
306
|
+
feature: doc.feature,
|
|
307
|
+
status: 'stub',
|
|
308
|
+
size: stats.size
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
return missing;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Generate documentation review report
|
|
319
|
+
*/
|
|
320
|
+
function generateReviewReport() {
|
|
321
|
+
console.log('Starting documentation files review...\n');
|
|
322
|
+
|
|
323
|
+
const files = getMarkdownFiles(DOCS_DIR);
|
|
324
|
+
console.log(`Found ${files.length} markdown files\n`);
|
|
325
|
+
|
|
326
|
+
const fileAnalyses = [];
|
|
327
|
+
let totalBrokenLinks = 0;
|
|
328
|
+
let totalOutdatedIssues = 0;
|
|
329
|
+
let filesNeedingReview = 0;
|
|
330
|
+
|
|
331
|
+
files.forEach(file => {
|
|
332
|
+
console.log(`Reviewing: ${path.relative(ROOT_DIR, file)}`);
|
|
333
|
+
const analysis = analyzeDocFile(file);
|
|
334
|
+
fileAnalyses.push(analysis);
|
|
335
|
+
|
|
336
|
+
totalBrokenLinks += analysis.brokenLinks.length;
|
|
337
|
+
totalOutdatedIssues += analysis.outdatedIssues.length;
|
|
338
|
+
if (analysis.needsReview) {
|
|
339
|
+
filesNeedingReview++;
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
// Check for missing documentation
|
|
344
|
+
const missingDocs = checkMissingDocumentation();
|
|
345
|
+
|
|
346
|
+
const report = {
|
|
347
|
+
reviewDate: new Date().toISOString(),
|
|
348
|
+
summary: {
|
|
349
|
+
totalFiles: files.length,
|
|
350
|
+
filesNeedingReview,
|
|
351
|
+
totalBrokenLinks,
|
|
352
|
+
totalOutdatedIssues,
|
|
353
|
+
missingDocumentation: missingDocs.length
|
|
354
|
+
},
|
|
355
|
+
missingDocumentation: missingDocs,
|
|
356
|
+
fileAnalyses
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
// Ensure output directory exists
|
|
360
|
+
const outputDir = path.dirname(OUTPUT_FILE);
|
|
361
|
+
if (!fs.existsSync(outputDir)) {
|
|
362
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Write report to file
|
|
366
|
+
fs.writeFileSync(OUTPUT_FILE, JSON.stringify(report, null, 2));
|
|
367
|
+
|
|
368
|
+
// Print summary
|
|
369
|
+
console.log('\n' + '='.repeat(60));
|
|
370
|
+
console.log('DOCUMENTATION REVIEW SUMMARY');
|
|
371
|
+
console.log('='.repeat(60));
|
|
372
|
+
console.log(`Total Files Reviewed: ${report.summary.totalFiles}`);
|
|
373
|
+
console.log(`Files Needing Review: ${report.summary.filesNeedingReview}`);
|
|
374
|
+
console.log(`Broken Links: ${report.summary.totalBrokenLinks}`);
|
|
375
|
+
console.log(`Outdated Content Issues: ${report.summary.totalOutdatedIssues}`);
|
|
376
|
+
console.log(`Missing Documentation: ${report.summary.missingDocumentation}`);
|
|
377
|
+
console.log('='.repeat(60));
|
|
378
|
+
|
|
379
|
+
if (missingDocs.length > 0) {
|
|
380
|
+
console.log('\nMissing or Stub Documentation:');
|
|
381
|
+
missingDocs.forEach(doc => {
|
|
382
|
+
console.log(` - ${doc.feature} (${doc.path}) - ${doc.status}`);
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (totalBrokenLinks > 0) {
|
|
387
|
+
console.log('\nFiles with Broken Links:');
|
|
388
|
+
fileAnalyses.forEach(file => {
|
|
389
|
+
if (file.brokenLinks.length > 0) {
|
|
390
|
+
console.log(` - ${file.filePath} (${file.brokenLinks.length} broken links)`);
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
console.log(`\nDetailed report saved to: ${OUTPUT_FILE}\n`);
|
|
396
|
+
|
|
397
|
+
return report;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Run the review
|
|
401
|
+
try {
|
|
402
|
+
generateReviewReport();
|
|
403
|
+
} catch (error) {
|
|
404
|
+
console.error('Error during review:', error);
|
|
405
|
+
process.exit(1);
|
|
406
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Development Environment Setup Script
|
|
4
|
+
# This script sets up the development environment for contributors
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
echo "=========================================="
|
|
9
|
+
echo "Setting up development environment..."
|
|
10
|
+
echo "=========================================="
|
|
11
|
+
echo ""
|
|
12
|
+
|
|
13
|
+
# Check if we're in the right directory
|
|
14
|
+
if [ ! -f "package.json" ]; then
|
|
15
|
+
echo "❌ Error: package.json not found. Please run this script from the repository root."
|
|
16
|
+
exit 1
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# Install dependencies
|
|
20
|
+
echo "📦 Installing dependencies..."
|
|
21
|
+
npm install
|
|
22
|
+
echo "✅ Dependencies installed"
|
|
23
|
+
echo ""
|
|
24
|
+
|
|
25
|
+
# Make pre-commit hook executable
|
|
26
|
+
if [ -f ".git/hooks/pre-commit" ]; then
|
|
27
|
+
echo "🔧 Setting up pre-commit hook..."
|
|
28
|
+
chmod +x .git/hooks/pre-commit
|
|
29
|
+
echo "✅ Pre-commit hook is now executable"
|
|
30
|
+
echo ""
|
|
31
|
+
|
|
32
|
+
# Test the hook
|
|
33
|
+
echo "🧪 Testing pre-commit hook..."
|
|
34
|
+
if .git/hooks/pre-commit; then
|
|
35
|
+
echo "✅ Pre-commit hook test passed!"
|
|
36
|
+
else
|
|
37
|
+
echo "⚠️ Pre-commit hook test failed. Please check documentation validation."
|
|
38
|
+
fi
|
|
39
|
+
else
|
|
40
|
+
echo "⚠️ Warning: Pre-commit hook not found at .git/hooks/pre-commit"
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
echo ""
|
|
44
|
+
echo "=========================================="
|
|
45
|
+
echo "Development environment setup complete!"
|
|
46
|
+
echo "=========================================="
|
|
47
|
+
echo ""
|
|
48
|
+
echo "Next steps:"
|
|
49
|
+
echo " 1. Review CONTRIBUTING.md for contribution guidelines"
|
|
50
|
+
echo " 2. Review .kiro/steering/documentation-standards.md for documentation requirements"
|
|
51
|
+
echo " 3. Run 'npm test' to ensure all tests pass"
|
|
52
|
+
echo " 4. Make your changes and commit (pre-commit hook will validate)"
|
|
53
|
+
echo ""
|
|
54
|
+
echo "Useful commands:"
|
|
55
|
+
echo " npm test - Run all tests"
|
|
56
|
+
echo " npm test -- test/documentation/ - Run documentation tests"
|
|
57
|
+
echo " node scripts/audit-documentation.mjs - Run documentation audit"
|
|
58
|
+
echo " .git/hooks/pre-commit - Test pre-commit hook manually"
|
|
59
|
+
echo ""
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Verification script for example implementation files
|
|
5
|
+
* Checks:
|
|
6
|
+
* - CloudFormation templates are syntactically valid YAML
|
|
7
|
+
* - Example code files use current API
|
|
8
|
+
* - Example code files have necessary imports
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import fs from 'fs';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import yaml from 'js-yaml';
|
|
14
|
+
import { fileURLToPath } from 'url';
|
|
15
|
+
|
|
16
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
+
const __dirname = path.dirname(__filename);
|
|
18
|
+
const rootDir = path.resolve(__dirname, '..');
|
|
19
|
+
|
|
20
|
+
const results = {
|
|
21
|
+
cloudFormationTemplates: [],
|
|
22
|
+
exampleCodeFiles: [],
|
|
23
|
+
errors: [],
|
|
24
|
+
warnings: []
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// CloudFormation template files to check
|
|
28
|
+
const cfTemplates = [
|
|
29
|
+
'docs/00-example-implementation/example-template-lambda-function.yml',
|
|
30
|
+
'docs/00-example-implementation/example-template-s3-and-dynamodb-cache-store.yml',
|
|
31
|
+
'docs/00-example-implementation/example-template-parameters.yml'
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
// Example code files to check
|
|
35
|
+
const exampleCodeFiles = [
|
|
36
|
+
'docs/00-example-implementation/example-validations.js',
|
|
37
|
+
'docs/00-example-implementation/example-buildspec.yml'
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
console.log('Verifying example implementation files...\n');
|
|
41
|
+
|
|
42
|
+
// CloudFormation YAML schema with custom tags
|
|
43
|
+
const CF_SCHEMA = yaml.DEFAULT_SCHEMA.extend([
|
|
44
|
+
new yaml.Type('!Ref', { kind: 'scalar' }),
|
|
45
|
+
new yaml.Type('!GetAtt', { kind: 'scalar' }),
|
|
46
|
+
new yaml.Type('!Sub', { kind: 'scalar' }),
|
|
47
|
+
new yaml.Type('!Join', { kind: 'sequence' }),
|
|
48
|
+
new yaml.Type('!Select', { kind: 'sequence' }),
|
|
49
|
+
new yaml.Type('!Split', { kind: 'sequence' }),
|
|
50
|
+
new yaml.Type('!If', { kind: 'sequence' }),
|
|
51
|
+
new yaml.Type('!Equals', { kind: 'sequence' }),
|
|
52
|
+
new yaml.Type('!Not', { kind: 'sequence' }),
|
|
53
|
+
new yaml.Type('!And', { kind: 'sequence' }),
|
|
54
|
+
new yaml.Type('!Or', { kind: 'sequence' }),
|
|
55
|
+
new yaml.Type('!FindInMap', { kind: 'sequence' }),
|
|
56
|
+
new yaml.Type('!GetAZs', { kind: 'scalar' }),
|
|
57
|
+
new yaml.Type('!ImportValue', { kind: 'scalar' }),
|
|
58
|
+
new yaml.Type('!Base64', { kind: 'scalar' })
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
// Check CloudFormation templates
|
|
62
|
+
console.log('Checking CloudFormation templates:');
|
|
63
|
+
for (const templatePath of cfTemplates) {
|
|
64
|
+
const fullPath = path.join(rootDir, templatePath);
|
|
65
|
+
try {
|
|
66
|
+
const content = fs.readFileSync(fullPath, 'utf8');
|
|
67
|
+
yaml.load(content, { schema: CF_SCHEMA });
|
|
68
|
+
console.log(` ✓ ${templatePath} - Valid YAML`);
|
|
69
|
+
results.cloudFormationTemplates.push({
|
|
70
|
+
file: templatePath,
|
|
71
|
+
valid: true
|
|
72
|
+
});
|
|
73
|
+
} catch (error) {
|
|
74
|
+
console.log(` ✗ ${templatePath} - Invalid YAML: ${error.message}`);
|
|
75
|
+
results.cloudFormationTemplates.push({
|
|
76
|
+
file: templatePath,
|
|
77
|
+
valid: false,
|
|
78
|
+
error: error.message
|
|
79
|
+
});
|
|
80
|
+
results.errors.push(`CloudFormation template ${templatePath}: ${error.message}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
console.log('\nChecking example code files:');
|
|
85
|
+
for (const codePath of exampleCodeFiles) {
|
|
86
|
+
const fullPath = path.join(rootDir, codePath);
|
|
87
|
+
try {
|
|
88
|
+
if (!fs.existsSync(fullPath)) {
|
|
89
|
+
console.log(` ! ${codePath} - File does not exist`);
|
|
90
|
+
results.warnings.push(`Example file ${codePath} does not exist`);
|
|
91
|
+
results.exampleCodeFiles.push({
|
|
92
|
+
file: codePath,
|
|
93
|
+
exists: false
|
|
94
|
+
});
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const content = fs.readFileSync(fullPath, 'utf8');
|
|
99
|
+
|
|
100
|
+
// Check for necessary imports if it's a .js file
|
|
101
|
+
if (codePath.endsWith('.js')) {
|
|
102
|
+
const hasRequire = content.includes('require(');
|
|
103
|
+
const hasImport = content.includes('import ');
|
|
104
|
+
const hasCacheDataImport = content.includes('@63klabs/cache-data');
|
|
105
|
+
|
|
106
|
+
if (!hasRequire && !hasImport) {
|
|
107
|
+
console.log(` ! ${codePath} - No imports found`);
|
|
108
|
+
results.warnings.push(`Example file ${codePath} has no imports`);
|
|
109
|
+
} else if (hasCacheDataImport) {
|
|
110
|
+
console.log(` ✓ ${codePath} - Has cache-data import`);
|
|
111
|
+
} else {
|
|
112
|
+
console.log(` ✓ ${codePath} - Has imports (no cache-data import)`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
results.exampleCodeFiles.push({
|
|
116
|
+
file: codePath,
|
|
117
|
+
exists: true,
|
|
118
|
+
hasImports: hasRequire || hasImport,
|
|
119
|
+
hasCacheDataImport: hasCacheDataImport
|
|
120
|
+
});
|
|
121
|
+
} else {
|
|
122
|
+
// For YAML files, just check they're valid
|
|
123
|
+
yaml.load(content);
|
|
124
|
+
console.log(` ✓ ${codePath} - Valid YAML`);
|
|
125
|
+
results.exampleCodeFiles.push({
|
|
126
|
+
file: codePath,
|
|
127
|
+
exists: true,
|
|
128
|
+
valid: true
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.log(` ✗ ${codePath} - Error: ${error.message}`);
|
|
133
|
+
results.errors.push(`Example file ${codePath}: ${error.message}`);
|
|
134
|
+
results.exampleCodeFiles.push({
|
|
135
|
+
file: codePath,
|
|
136
|
+
exists: true,
|
|
137
|
+
error: error.message
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Check for missing example files that are referenced in README
|
|
143
|
+
const missingFiles = [
|
|
144
|
+
'docs/00-example-implementation/example-config.js',
|
|
145
|
+
'docs/00-example-implementation/example-handler.js'
|
|
146
|
+
];
|
|
147
|
+
|
|
148
|
+
console.log('\nChecking for referenced but missing files:');
|
|
149
|
+
for (const filePath of missingFiles) {
|
|
150
|
+
const fullPath = path.join(rootDir, filePath);
|
|
151
|
+
if (!fs.existsSync(fullPath)) {
|
|
152
|
+
console.log(` ! ${filePath} - Referenced in README but does not exist`);
|
|
153
|
+
results.warnings.push(`Referenced file ${filePath} does not exist (noted in README as missing)`);
|
|
154
|
+
} else {
|
|
155
|
+
const content = fs.readFileSync(fullPath, 'utf8');
|
|
156
|
+
if (content.trim() === '') {
|
|
157
|
+
console.log(` ! ${filePath} - File exists but is empty`);
|
|
158
|
+
results.warnings.push(`Referenced file ${filePath} exists but is empty`);
|
|
159
|
+
} else {
|
|
160
|
+
console.log(` ✓ ${filePath} - Exists and has content`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Summary
|
|
166
|
+
console.log('\n' + '='.repeat(60));
|
|
167
|
+
console.log('VERIFICATION SUMMARY');
|
|
168
|
+
console.log('='.repeat(60));
|
|
169
|
+
console.log(`CloudFormation templates checked: ${results.cloudFormationTemplates.length}`);
|
|
170
|
+
console.log(`Example code files checked: ${results.exampleCodeFiles.length}`);
|
|
171
|
+
console.log(`Errors: ${results.errors.length}`);
|
|
172
|
+
console.log(`Warnings: ${results.warnings.length}`);
|
|
173
|
+
|
|
174
|
+
if (results.errors.length > 0) {
|
|
175
|
+
console.log('\nERRORS:');
|
|
176
|
+
results.errors.forEach(err => console.log(` - ${err}`));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (results.warnings.length > 0) {
|
|
180
|
+
console.log('\nWARNINGS:');
|
|
181
|
+
results.warnings.forEach(warn => console.log(` - ${warn}`));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Exit with error code if there are errors
|
|
185
|
+
if (results.errors.length > 0) {
|
|
186
|
+
console.log('\n❌ Verification failed with errors');
|
|
187
|
+
process.exit(1);
|
|
188
|
+
} else if (results.warnings.length > 0) {
|
|
189
|
+
console.log('\n⚠️ Verification completed with warnings');
|
|
190
|
+
process.exit(0);
|
|
191
|
+
} else {
|
|
192
|
+
console.log('\n✅ All verifications passed');
|
|
193
|
+
process.exit(0);
|
|
194
|
+
}
|