Initial Commit

This commit is contained in:
2026-09-16 17:22:14 +09:00
commit 858ee9e9da
335 changed files with 123898 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
+24
View File
@@ -0,0 +1,24 @@
// crypto.randomUUID() 는 secure context (HTTPS / localhost) 전용.
// HTTP 환경에서도 깨지지 않도록 fallback 둠.
//
// 1) crypto.randomUUID() 가능하면 그대로 — RFC4122 v4 보장
// 2) 안 되면 crypto.getRandomValues() 로 v4 형식 직접 조립 (HTTP에서도 동작)
// 3) 최후 fallback — Math.random + Date.now (충돌 가능성 있지만 메시지 id 용도면 충분)
export function randomId(): string {
const c = globalThis.crypto
if (c?.randomUUID) {
return c.randomUUID()
}
if (c?.getRandomValues) {
const bytes = new Uint8Array(16)
c.getRandomValues(bytes)
bytes[6] = (bytes[6] & 0x0f) | 0x40 // version 4
bytes[8] = (bytes[8] & 0x3f) | 0x80 // variant 10
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("")
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`
}
return `${Date.now().toString(16)}-${Math.random().toString(16).slice(2, 10)}`
}
@@ -0,0 +1,11 @@
import { describe, it, expect } from "vitest"
import { formatRelativeKo } from "./relativeTime"
describe("formatRelativeKo", () => {
it("몇 분 전 한글 접미사를 낸다", () => {
const fiveMinAgo = new Date(Date.now() - 5 * 60_000).toISOString()
const out = formatRelativeKo(fiveMinAgo)
expect(out).toContain("분")
expect(out).toContain("전")
})
})
+7
View File
@@ -0,0 +1,7 @@
import { formatDistanceToNow } from "date-fns"
import { ko } from "date-fns/locale"
/** ISO 시각을 "5분 전" 같은 한글 상대시간으로. */
export function formatRelativeKo(iso: string): string {
return formatDistanceToNow(new Date(iso), { addSuffix: true, locale: ko })
}
+3
View File
@@ -0,0 +1,3 @@
export function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms))
}