Skip to content

Commit 5fe44a2

Browse files
author
Gerome El-assaad
committed
Add utility libraries for tasks and logging
1 parent 23c1f7a commit 5fe44a2

5 files changed

Lines changed: 519 additions & 0 deletions

File tree

lib/cookies.ts

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
const SIDEBAR_WIDTH_COOKIE = 'sidebar-width'
2+
const SIDEBAR_OPEN_COOKIE = 'sidebar-open'
3+
const DEFAULT_SIDEBAR_WIDTH = 288
4+
const DEFAULT_SIDEBAR_OPEN = false // Default to false to avoid hydration issues
5+
6+
// Simple cookie utilities using native browser APIs
7+
function getCookie(name: string): string | undefined {
8+
if (typeof window === 'undefined') return undefined
9+
10+
const value = `; ${document.cookie}`
11+
const parts = value.split(`; ${name}=`)
12+
if (parts.length === 2) {
13+
return parts.pop()?.split(';').shift()
14+
}
15+
return undefined
16+
}
17+
18+
function setCookie(name: string, value: string, days: number = 365): void {
19+
if (typeof window === 'undefined') return
20+
21+
const expires = new Date()
22+
expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000)
23+
document.cookie = `${name}=${value};expires=${expires.toUTCString()};path=/;SameSite=strict`
24+
}
25+
26+
export function getSidebarWidth(): number {
27+
if (typeof window === 'undefined') {
28+
// Server-side: return default
29+
return DEFAULT_SIDEBAR_WIDTH
30+
}
31+
32+
const cookieValue = getCookie(SIDEBAR_WIDTH_COOKIE)
33+
if (cookieValue) {
34+
const width = parseInt(cookieValue, 10)
35+
if (!isNaN(width) && width >= 200 && width <= 600) {
36+
return width
37+
}
38+
}
39+
40+
return DEFAULT_SIDEBAR_WIDTH
41+
}
42+
43+
export function setSidebarWidth(width: number): void {
44+
if (typeof window === 'undefined') return
45+
46+
// Validate width
47+
if (width >= 200 && width <= 600) {
48+
setCookie(SIDEBAR_WIDTH_COOKIE, width.toString())
49+
}
50+
}
51+
52+
export function getSidebarWidthFromCookie(cookieString?: string): number {
53+
if (!cookieString) return DEFAULT_SIDEBAR_WIDTH
54+
55+
const cookies = cookieString
56+
.split(';')
57+
.map((cookie) => cookie.trim().split('='))
58+
.reduce(
59+
(acc, [key, value]) => {
60+
acc[key] = value
61+
return acc
62+
},
63+
{} as Record<string, string>,
64+
)
65+
66+
const width = parseInt(cookies[SIDEBAR_WIDTH_COOKIE] || '', 10)
67+
if (!isNaN(width) && width >= 200 && width <= 600) {
68+
return width
69+
}
70+
71+
return DEFAULT_SIDEBAR_WIDTH
72+
}
73+
74+
// Sidebar open/closed state functions
75+
export function getSidebarOpen(): boolean {
76+
if (typeof window === 'undefined') {
77+
return DEFAULT_SIDEBAR_OPEN
78+
}
79+
80+
const cookieValue = getCookie(SIDEBAR_OPEN_COOKIE)
81+
if (cookieValue) {
82+
return cookieValue === 'true'
83+
}
84+
85+
return DEFAULT_SIDEBAR_OPEN
86+
}
87+
88+
export function setSidebarOpen(isOpen: boolean): void {
89+
if (typeof window === 'undefined') return
90+
91+
setCookie(SIDEBAR_OPEN_COOKIE, isOpen.toString())
92+
}
93+
94+
export function getSidebarOpenFromCookie(cookieString?: string): boolean {
95+
if (!cookieString) return DEFAULT_SIDEBAR_OPEN
96+
97+
const cookies = cookieString
98+
.split(';')
99+
.map((cookie) => cookie.trim().split('='))
100+
.reduce(
101+
(acc, [key, value]) => {
102+
acc[key] = value
103+
return acc
104+
},
105+
{} as Record<string, string>,
106+
)
107+
108+
const isOpen = cookies[SIDEBAR_OPEN_COOKIE]
109+
if (isOpen !== undefined) {
110+
return isOpen === 'true'
111+
}
112+
113+
return DEFAULT_SIDEBAR_OPEN
114+
}

lib/id.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { nanoid } from 'nanoid'
2+
3+
export function generateId(length: number = 12): string {
4+
return nanoid(length)
5+
}

lib/llm-guide.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import fs from 'fs'
2+
import path from 'path'
3+
4+
export function getLLMGuide(): string {
5+
try {
6+
const llmGuidePath = path.join(process.cwd(), 'LLM.txt')
7+
return fs.readFileSync(llmGuidePath, 'utf-8')
8+
} catch (error) {
9+
console.error('Failed to read LLM.txt:', error)
10+
return `You are CodinIT.dev, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.`
11+
}
12+
}
13+
14+
export function getLLMSection(content: string, sectionName: string): string {
15+
const sections = content.split('====')
16+
const section = sections.find(s =>
17+
s.trim().startsWith(sectionName) ||
18+
s.includes(sectionName)
19+
)
20+
return section ? section.trim() : ''
21+
}
22+
23+
/**
24+
* Gets template-specific instructions from the LLM guide
25+
*/
26+
export function getTemplateInstructions(templateId: string): string {
27+
const guide = getLLMGuide()
28+
29+
// Find template-specific instructions section
30+
const sections = guide.split('====')
31+
const templateSection = sections.find(s =>
32+
s.trim().includes('TEMPLATE-SPECIFIC INSTRUCTIONS')
33+
)
34+
35+
if (!templateSection) return ''
36+
37+
// Extract specific template instructions
38+
const templateName = templateId.replace('-', ' ').toLowerCase()
39+
const lines = templateSection.split('\n')
40+
const startIndex = lines.findIndex(line =>
41+
line.toLowerCase().includes(templateName) ||
42+
line.toLowerCase().includes(templateId)
43+
)
44+
45+
if (startIndex === -1) return ''
46+
47+
// Get instructions until next template or section
48+
let endIndex = lines.findIndex((line, index) =>
49+
index > startIndex &&
50+
(line.startsWith('##') || line.startsWith('===='))
51+
)
52+
53+
if (endIndex === -1) endIndex = lines.length
54+
55+
return lines.slice(startIndex, endIndex).join('\n').trim()
56+
}

lib/logging.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { LogEntry } from '@/lib/schema'
2+
import { nanoid } from 'nanoid'
3+
4+
export type { LogEntry }
5+
6+
export function redactSensitiveInfo(message: string): string {
7+
let redacted = message
8+
9+
const apiKeyPatterns = [
10+
// Anthropic API keys (sk-ant-...)
11+
/ANTHROPIC_API_KEY[=\s]*["']?(sk-ant-[a-zA-Z0-9_-]{20,})/gi,
12+
// OpenAI API keys (sk-...)
13+
/OPENAI_API_KEY[=\s]*["']?([sk-][a-zA-Z0-9_-]{20,})/gi,
14+
// GitHub tokens (ghp_, gho_, ghu_, ghs_, ghr_)
15+
/GITHUB_TOKEN[=\s]*["']?([gh][phosr]_[a-zA-Z0-9_]{20,})/gi,
16+
// Generic API key patterns
17+
/API_KEY[=\s]*["']?([a-zA-Z0-9_-]{20,})/gi,
18+
// Bearer tokens
19+
/Bearer\s+([a-zA-Z0-9_-]{20,})/gi,
20+
// Generic tokens
21+
/TOKEN[=\s]*["']?([a-zA-Z0-9_-]{20,})/gi,
22+
]
23+
24+
// Apply redaction patterns
25+
apiKeyPatterns.forEach((pattern) => {
26+
redacted = redacted.replace(pattern, (match, key) => {
27+
// Keep the prefix and show first 4 and last 4 characters
28+
const prefix = match.substring(0, match.indexOf(key))
29+
const redactedKey =
30+
key.length > 8
31+
? `${key.substring(0, 4)}${'*'.repeat(Math.max(8, key.length - 8))}${key.substring(key.length - 4)}`
32+
: '*'.repeat(key.length)
33+
return `${prefix}${redactedKey}`
34+
})
35+
})
36+
37+
// Redact environment variable assignments with sensitive values
38+
redacted = redacted.replace(
39+
/([A-Z_]*(?:KEY|TOKEN|SECRET|PASSWORD)[A-Z_]*)[=\s]*["']?([a-zA-Z0-9_-]{8,})["']?/gi,
40+
(match, varName, value) => {
41+
const redactedValue =
42+
value.length > 8
43+
? `${value.substring(0, 4)}${'*'.repeat(Math.max(8, value.length - 8))}${value.substring(value.length - 4)}`
44+
: '*'.repeat(value.length)
45+
return `${varName}="${redactedValue}"`
46+
},
47+
)
48+
49+
return redacted
50+
}
51+
52+
export function createLogEntry(type: LogEntry['type'], message: string, timestamp?: Date): LogEntry {
53+
return {
54+
id: nanoid(),
55+
type,
56+
message: redactSensitiveInfo(message),
57+
timestamp: (timestamp || new Date()).toISOString(),
58+
}
59+
}
60+
61+
export function createInfoLog(message: string): LogEntry {
62+
return createLogEntry('info', message)
63+
}
64+
65+
export function createCommandLog(command: string, args?: string[]): LogEntry {
66+
const fullCommand = args ? `${command} ${args.join(' ')}` : command
67+
return createLogEntry('command', `$ ${fullCommand}`)
68+
}
69+
70+
export function createErrorLog(message: string): LogEntry {
71+
return createLogEntry('error', message)
72+
}
73+
74+
export function createSuccessLog(message: string): LogEntry {
75+
return createLogEntry('success', message)
76+
}

0 commit comments

Comments
 (0)