@m1kapp/kit 0.0.26 → 0.0.28

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/README.md CHANGED
@@ -750,6 +750,42 @@ export const GET = handler(async () => {
750
750
  });
751
751
  ```
752
752
 
753
+ ### 서버 유틸 (의존성 0)
754
+
755
+ API 라우트에서 자주 손으로 만들던 것들 — 전부 `@m1kapp/kit/server`에 있어요.
756
+
757
+ ```ts
758
+ import {
759
+ requireEnv, fetchWithRetry, withRetry, recoverJsonFromText,
760
+ scrapeOg, todayKST, dateInTz, idToSlug, slugToId, appHost,
761
+ } from "@m1kapp/kit/server";
762
+
763
+ // 필수 env 검증 (없으면 500 throw, 있으면 타입된 객체)
764
+ const { XAI_API_KEY } = requireEnv(["XAI_API_KEY"]);
765
+
766
+ // fetch + 타임아웃 + 429/5xx 자동 재시도 (마지막 응답 반환)
767
+ const res = await fetchWithRetry(url, { headers: { authorization: `Bearer ${XAI_API_KEY}` } });
768
+
769
+ // 아무 async나 재시도 (Neon 콜드스타트 등)
770
+ const rows = await withRetry(() => db.query.users.findMany(), {
771
+ shouldRetry: (e) => String(e).includes("fetch failed"),
772
+ });
773
+
774
+ // LLM 응답에서 JSON 복구 (```json 펜스·트레일링 콤마·노이즈 제거)
775
+ const data = recoverJsonFromText<{ items: string[] }>(llmReply);
776
+
777
+ // Open Graph 스크래핑
778
+ const og = await scrapeOg("example.com"); // { title, description, image, siteName, url }
779
+
780
+ // 타임존 날짜
781
+ todayKST(); // "2026-06-04"
782
+ dateInTz(Date.now(), "America/New_York");
783
+
784
+ // base62 slug + 호스트
785
+ idToSlug(42, 1000); // "..." (slugToId로 역변환)
786
+ const base = `https://${appHost("m1k.app")}`;
787
+ ```
788
+
753
789
  ---
754
790
 
755
791
  ## Utils
package/bin/stats.mjs CHANGED
@@ -155,11 +155,10 @@ for (const name of allImports) {
155
155
  // 카테고리별 사용 수 (loc 0이어도 카운트 — "Tab"도 사용한 거니까)
156
156
  usedByCategory[meta.category] = (usedByCategory[meta.category] || 0) + 1;
157
157
 
158
- // LOC 절약은 소스 파일 단위로 1번만
159
- if (meta.source && countedSources.has(meta.source)) continue;
160
- if (meta.source) countedSources.add(meta.source);
161
-
162
- if (meta.loc > 0) {
158
+ // LOC 절약은 소스 파일 단위로 1번만. 대표(loc>0)만 카운트하므로 import
159
+ // 순서와 무관 — 비대표(loc 0) 먼저 와도 source를 선점하지 않는다.
160
+ if (meta.loc > 0 && !(meta.source && countedSources.has(meta.source))) {
161
+ if (meta.source) countedSources.add(meta.source);
163
162
  usedFeatures.push({ name, loc: meta.loc, category: meta.category });
164
163
  savedLines += meta.loc;
165
164
  }
package/dist/index.d.mts CHANGED
@@ -49,15 +49,24 @@ interface AppShellProps {
49
49
  * Accent color (any CSS color) propagated to kit components as `--kit-accent`.
50
50
  * Lets you re-skin the whole shell — Switch, SegmentedControl, ChatBubble,
51
51
  * ActionCard, ListRow… — in one place. e.g. accent="#e2603f"
52
+ *
53
+ * When `accent` is a hex color, a contrasting foreground (`--kit-accent-fg`,
54
+ * black/white) is derived automatically so labels on the accent stay legible.
52
55
  */
53
56
  accent?: string;
57
+ /**
58
+ * Foreground color used *on top of* the accent (`--kit-accent-fg`). Defaults
59
+ * to an auto-derived black/white for hex accents, else white. Set this when
60
+ * `accent` is a non-hex CSS color (named/rgb/hsl) and the default contrasts poorly.
61
+ */
62
+ accentFg?: string;
54
63
  style?: CSSProperties;
55
64
  }
56
65
  /**
57
66
  * Mobile app-like container with rounded corners, shadow, and ring.
58
67
  * Centers content and constrains width for a phone-like viewport.
59
68
  */
60
- declare function AppShell({ children, className, maxWidth, maxHeight, accent, style, }: AppShellProps): react_jsx_runtime.JSX.Element;
69
+ declare function AppShell({ children, className, maxWidth, maxHeight, accent, accentFg, style, }: AppShellProps): react_jsx_runtime.JSX.Element;
61
70
  interface AppShellHeaderProps {
62
71
  children: ReactNode;
63
72
  className?: string;
package/dist/index.d.ts CHANGED
@@ -49,15 +49,24 @@ interface AppShellProps {
49
49
  * Accent color (any CSS color) propagated to kit components as `--kit-accent`.
50
50
  * Lets you re-skin the whole shell — Switch, SegmentedControl, ChatBubble,
51
51
  * ActionCard, ListRow… — in one place. e.g. accent="#e2603f"
52
+ *
53
+ * When `accent` is a hex color, a contrasting foreground (`--kit-accent-fg`,
54
+ * black/white) is derived automatically so labels on the accent stay legible.
52
55
  */
53
56
  accent?: string;
57
+ /**
58
+ * Foreground color used *on top of* the accent (`--kit-accent-fg`). Defaults
59
+ * to an auto-derived black/white for hex accents, else white. Set this when
60
+ * `accent` is a non-hex CSS color (named/rgb/hsl) and the default contrasts poorly.
61
+ */
62
+ accentFg?: string;
54
63
  style?: CSSProperties;
55
64
  }
56
65
  /**
57
66
  * Mobile app-like container with rounded corners, shadow, and ring.
58
67
  * Centers content and constrains width for a phone-like viewport.
59
68
  */
60
- declare function AppShell({ children, className, maxWidth, maxHeight, accent, style, }: AppShellProps): react_jsx_runtime.JSX.Element;
69
+ declare function AppShell({ children, className, maxWidth, maxHeight, accent, accentFg, style, }: AppShellProps): react_jsx_runtime.JSX.Element;
61
70
  interface AppShellHeaderProps {
62
71
  children: ReactNode;
63
72
  className?: string;