@m1kapp/kit 0.0.2 → 0.0.4
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/dist/index.d.mts +33 -181
- package/dist/index.d.ts +33 -181
- package/dist/index.js +4 -4
- package/dist/index.mjs +4 -4
- package/dist/ogimage.d.mts +175 -0
- package/dist/ogimage.d.ts +175 -0
- package/dist/ogimage.js +1 -0
- package/dist/ogimage.mjs +1 -0
- package/dist/server.d.mts +29 -10
- package/dist/server.d.ts +29 -10
- package/dist/server.js +1 -1
- package/dist/server.mjs +1 -1
- package/package.json +6 -1
package/dist/index.d.mts
CHANGED
|
@@ -438,8 +438,14 @@ interface UseFormSubmitOptions<T> {
|
|
|
438
438
|
onSuccess?: (data: T) => void;
|
|
439
439
|
onError?: (err: Error) => void;
|
|
440
440
|
}
|
|
441
|
+
/**
|
|
442
|
+
* Submit function type:
|
|
443
|
+
* - When V is `void` (fn takes no arguments) → `submit()` needs no args
|
|
444
|
+
* - Otherwise → `submit(vars: V)` requires the arg
|
|
445
|
+
*/
|
|
446
|
+
type SubmitFn<V> = V extends void ? () => Promise<void> : (vars: V) => Promise<void>;
|
|
441
447
|
interface UseFormSubmitResult<T, V> {
|
|
442
|
-
submit:
|
|
448
|
+
submit: SubmitFn<V>;
|
|
443
449
|
loading: boolean;
|
|
444
450
|
error: Error | null;
|
|
445
451
|
data: T | undefined;
|
|
@@ -450,16 +456,21 @@ interface UseFormSubmitResult<T, V> {
|
|
|
450
456
|
* Eliminates the try/catch/finally + setState boilerplate from every form.
|
|
451
457
|
*
|
|
452
458
|
* @example
|
|
459
|
+
* // With args
|
|
453
460
|
* const { submit, loading, error } = useFormSubmit(async (url: string) => {
|
|
454
461
|
* return api.post<Site>("/api/sites", { url });
|
|
455
462
|
* }, {
|
|
456
|
-
* onSuccess: () => router.push(
|
|
463
|
+
* onSuccess: (site) => router.push(`/${site.slug}`),
|
|
457
464
|
* });
|
|
465
|
+
* <button onClick={() => submit(inputValue)}>등록</button>
|
|
458
466
|
*
|
|
459
|
-
*
|
|
460
|
-
*
|
|
461
|
-
*
|
|
462
|
-
*
|
|
467
|
+
* @example
|
|
468
|
+
* // No args (fire-and-forget style)
|
|
469
|
+
* const { submit: save, loading } = useFormSubmit(
|
|
470
|
+
* () => api.put("/api/sites/settings", config),
|
|
471
|
+
* { onSuccess: () => setSaved(true) }
|
|
472
|
+
* );
|
|
473
|
+
* <button onClick={save}>저장</button>
|
|
463
474
|
*/
|
|
464
475
|
declare function useFormSubmit<T, V = void>(fn: (vars: V) => Promise<T>, options?: UseFormSubmitOptions<T>): UseFormSubmitResult<T, V>;
|
|
465
476
|
|
|
@@ -542,178 +553,6 @@ interface DialogProps {
|
|
|
542
553
|
*/
|
|
543
554
|
declare function Dialog({ open, onClose, title, size, children, persistent, className, }: DialogProps): React__default.ReactPortal | null;
|
|
544
555
|
|
|
545
|
-
interface OGConfig {
|
|
546
|
-
/** 앱 이름 (좌상단 로고) */
|
|
547
|
-
appName?: string;
|
|
548
|
-
/** 브랜드 색상 (hex) */
|
|
549
|
-
color?: string;
|
|
550
|
-
/** 하단 도메인 */
|
|
551
|
-
domain?: string;
|
|
552
|
-
/** 배경 스타일 — "dark"(기본) | "gradient"(다크 오로라) | "blend"(라이트 파스텔 블렌드) */
|
|
553
|
-
bg?: "dark" | "gradient" | "blend";
|
|
554
|
-
/** 로고 이미지 URL (지정 시 appName 첫 글자 대신 이미지 표시) */
|
|
555
|
-
logoUrl?: string;
|
|
556
|
-
}
|
|
557
|
-
interface OGDefaultTemplate {
|
|
558
|
-
type?: "default";
|
|
559
|
-
title: string;
|
|
560
|
-
sub?: string;
|
|
561
|
-
badge?: string;
|
|
562
|
-
}
|
|
563
|
-
interface OGMatchTemplate {
|
|
564
|
-
type: "match";
|
|
565
|
-
home: string;
|
|
566
|
-
away: string;
|
|
567
|
-
score?: string;
|
|
568
|
-
sub?: string;
|
|
569
|
-
badge?: string;
|
|
570
|
-
}
|
|
571
|
-
interface OGSquareTemplate {
|
|
572
|
-
type: "square";
|
|
573
|
-
title: string;
|
|
574
|
-
sub?: string;
|
|
575
|
-
badge?: string;
|
|
576
|
-
}
|
|
577
|
-
interface OGIconTemplate {
|
|
578
|
-
type: "icon";
|
|
579
|
-
/** 아이콘에 표시할 글자 (기본: appName 첫 글자) */
|
|
580
|
-
letter?: string;
|
|
581
|
-
/** 아이콘 모서리 둥글기 (기본: 96) */
|
|
582
|
-
radius?: number;
|
|
583
|
-
}
|
|
584
|
-
interface OGArticleTemplate {
|
|
585
|
-
type: "article";
|
|
586
|
-
title: string;
|
|
587
|
-
/** 작성자 */
|
|
588
|
-
author?: string;
|
|
589
|
-
/** 날짜 또는 발행일 */
|
|
590
|
-
date?: string;
|
|
591
|
-
/** 카테고리 / 태그 */
|
|
592
|
-
category?: string;
|
|
593
|
-
sub?: string;
|
|
594
|
-
}
|
|
595
|
-
interface OGStatTemplate {
|
|
596
|
-
type: "stat";
|
|
597
|
-
/** 강조할 숫자 또는 지표 */
|
|
598
|
-
stat: string;
|
|
599
|
-
/** 지표 설명 */
|
|
600
|
-
label: string;
|
|
601
|
-
sub?: string;
|
|
602
|
-
badge?: string;
|
|
603
|
-
}
|
|
604
|
-
interface OGProductTemplate {
|
|
605
|
-
type: "product";
|
|
606
|
-
title: string;
|
|
607
|
-
tagline?: string;
|
|
608
|
-
/** 핵심 특징 (최대 3개) */
|
|
609
|
-
features?: string[];
|
|
610
|
-
badge?: string;
|
|
611
|
-
}
|
|
612
|
-
type OGTemplate = OGDefaultTemplate | OGMatchTemplate | OGSquareTemplate | OGIconTemplate | OGArticleTemplate | OGStatTemplate | OGProductTemplate;
|
|
613
|
-
type OGProps = OGTemplate & OGConfig;
|
|
614
|
-
declare function OGImage(props: OGProps): react_jsx_runtime.JSX.Element;
|
|
615
|
-
|
|
616
|
-
/** @vercel/og (Satori) fonts 옵션에 넘길 수 있는 폰트 정의 */
|
|
617
|
-
interface OGFont {
|
|
618
|
-
name: string;
|
|
619
|
-
data: ArrayBuffer;
|
|
620
|
-
weight: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;
|
|
621
|
-
style?: "normal" | "italic";
|
|
622
|
-
}
|
|
623
|
-
declare const PRETENDARD_WEIGHTS: {
|
|
624
|
-
readonly 400: "Pretendard-Regular.otf";
|
|
625
|
-
readonly 700: "Pretendard-Bold.otf";
|
|
626
|
-
readonly 900: "Pretendard-Black.otf";
|
|
627
|
-
};
|
|
628
|
-
type PretendardWeight = keyof typeof PRETENDARD_WEIGHTS;
|
|
629
|
-
/**
|
|
630
|
-
* Pretendard 폰트를 CDN에서 로드합니다.
|
|
631
|
-
*
|
|
632
|
-
* @example
|
|
633
|
-
* ```ts
|
|
634
|
-
* import { OG, loadPretendard } from "@m1kapp/seo";
|
|
635
|
-
* import { ImageResponse } from "next/og";
|
|
636
|
-
*
|
|
637
|
-
* export async function GET() {
|
|
638
|
-
* const fonts = await loadPretendard();
|
|
639
|
-
* return new ImageResponse(<OG type="default" title="Hello" />, {
|
|
640
|
-
* width: 1200, height: 630, fonts,
|
|
641
|
-
* });
|
|
642
|
-
* }
|
|
643
|
-
* ```
|
|
644
|
-
*
|
|
645
|
-
* @param weights 로드할 weight 배열 (기본: [700, 900])
|
|
646
|
-
*/
|
|
647
|
-
declare function loadPretendard(weights?: PretendardWeight[]): Promise<OGFont[]>;
|
|
648
|
-
/**
|
|
649
|
-
* 임의의 URL에서 폰트를 로드합니다.
|
|
650
|
-
*
|
|
651
|
-
* @example
|
|
652
|
-
* ```ts
|
|
653
|
-
* const fonts = await loadFont({
|
|
654
|
-
* name: "CustomFont",
|
|
655
|
-
* url: "https://example.com/CustomFont-Bold.otf",
|
|
656
|
-
* weight: 700,
|
|
657
|
-
* });
|
|
658
|
-
* ```
|
|
659
|
-
*/
|
|
660
|
-
declare function loadFont(opts: {
|
|
661
|
-
name: string;
|
|
662
|
-
url: string;
|
|
663
|
-
weight?: OGFont["weight"];
|
|
664
|
-
style?: OGFont["style"];
|
|
665
|
-
}): Promise<OGFont>;
|
|
666
|
-
/**
|
|
667
|
-
* Google Fonts에서 폰트를 로드합니다.
|
|
668
|
-
* CSS 응답을 파싱해 TTF/OTF URL을 자동 추출합니다.
|
|
669
|
-
*
|
|
670
|
-
* @example
|
|
671
|
-
* ```ts
|
|
672
|
-
* const fonts = await loadGoogleFont("Noto Sans KR", [400, 700]);
|
|
673
|
-
* return new ImageResponse(<OG ... />, { fonts });
|
|
674
|
-
* ```
|
|
675
|
-
*/
|
|
676
|
-
declare function loadGoogleFont(family: string, weights?: OGFont["weight"][]): Promise<OGFont[]>;
|
|
677
|
-
|
|
678
|
-
/**
|
|
679
|
-
* @vercel/og ImageResponse에서 지원하는 이모지 스타일.
|
|
680
|
-
* ImageResponse 옵션의 `emoji` 필드에 넘기면 됩니다.
|
|
681
|
-
*/
|
|
682
|
-
type EmojiStyle = "twemoji" | "openmoji" | "noto" | "fluent" | "fluentFlat" | "blobmoji";
|
|
683
|
-
/**
|
|
684
|
-
* raw Satori의 `loadAdditionalAsset` 콜백을 생성합니다.
|
|
685
|
-
*
|
|
686
|
-
* @example
|
|
687
|
-
* ```ts
|
|
688
|
-
* import satori from "satori";
|
|
689
|
-
* import { createEmojiLoader } from "@m1kapp/seo";
|
|
690
|
-
*
|
|
691
|
-
* const svg = await satori(<OG ... />, {
|
|
692
|
-
* width: 1200, height: 630,
|
|
693
|
-
* fonts: [...],
|
|
694
|
-
* loadAdditionalAsset: createEmojiLoader(),
|
|
695
|
-
* });
|
|
696
|
-
* ```
|
|
697
|
-
*/
|
|
698
|
-
declare function createEmojiLoader(): (code: string, segment: string) => Promise<string | undefined>;
|
|
699
|
-
|
|
700
|
-
/**
|
|
701
|
-
* OG 이미지 URL의 캐시를 즉시 무효화할 버전 키를 반환합니다.
|
|
702
|
-
*
|
|
703
|
-
* Cache-Control로 CDN에 캐싱된 OG 이미지를 강제 갱신하고 싶을 때
|
|
704
|
-
* URL의 `v` 파라미터에 이 값을 넣으세요.
|
|
705
|
-
*
|
|
706
|
-
* @example
|
|
707
|
-
* ```ts
|
|
708
|
-
* import { getOGVersion } from "@m1kapp/seo";
|
|
709
|
-
*
|
|
710
|
-
* // OG 갱신 버튼 핸들러
|
|
711
|
-
* const freshUrl = `/og?title=${title}&v=${getOGVersion()}`;
|
|
712
|
-
* // → /og?title=...&v=20260415152347
|
|
713
|
-
* ```
|
|
714
|
-
*/
|
|
715
|
-
declare function getOGVersion(): string;
|
|
716
|
-
|
|
717
556
|
type PWAInstallState = "android-ready" | "ios-safari" | "installed" | "unsupported";
|
|
718
557
|
interface UsePWAInstallReturn {
|
|
719
558
|
state: PWAInstallState;
|
|
@@ -816,7 +655,9 @@ declare class ApiError extends Error {
|
|
|
816
655
|
readonly status: number;
|
|
817
656
|
readonly statusText: string;
|
|
818
657
|
readonly body: unknown;
|
|
819
|
-
|
|
658
|
+
readonly url?: string | undefined;
|
|
659
|
+
readonly method?: string | undefined;
|
|
660
|
+
constructor(status: number, statusText: string, body: unknown, url?: string | undefined, method?: string | undefined);
|
|
820
661
|
}
|
|
821
662
|
|
|
822
663
|
interface ApiClientOptions {
|
|
@@ -824,7 +665,7 @@ interface ApiClientOptions {
|
|
|
824
665
|
headers?: Record<string, string>;
|
|
825
666
|
/** Called before every request — mutate or replace the Request */
|
|
826
667
|
onRequest?: (req: Request) => Request | void;
|
|
827
|
-
/** Called on every non-2xx response */
|
|
668
|
+
/** Called on every non-2xx response — includes url and method for debugging */
|
|
828
669
|
onError?: (err: ApiError) => void;
|
|
829
670
|
}
|
|
830
671
|
type RequestOptions = {
|
|
@@ -844,6 +685,7 @@ type ApiClient = ReturnType<typeof createApiClient>;
|
|
|
844
685
|
|
|
845
686
|
/** Manually invalidate cache entries. Pass a URL to clear one, or nothing to clear all. */
|
|
846
687
|
declare function clearFetchCache(url?: string): void;
|
|
688
|
+
type FetchStatus = "idle" | "loading" | "success" | "error";
|
|
847
689
|
interface UseFetchOptions<T> {
|
|
848
690
|
/** Skip fetching when false (default: true) */
|
|
849
691
|
enabled?: boolean;
|
|
@@ -864,6 +706,14 @@ interface UseFetchResult<T> {
|
|
|
864
706
|
data: T | undefined;
|
|
865
707
|
loading: boolean;
|
|
866
708
|
error: Error | undefined;
|
|
709
|
+
/**
|
|
710
|
+
* Lifecycle status:
|
|
711
|
+
* - `"idle"` — url is null/undefined or `enabled: false`
|
|
712
|
+
* - `"loading"` — fetch in progress
|
|
713
|
+
* - `"success"` — data loaded (note: data may be null if the API returned null)
|
|
714
|
+
* - `"error"` — last fetch failed
|
|
715
|
+
*/
|
|
716
|
+
status: FetchStatus;
|
|
867
717
|
/** Manually trigger a refetch (ignores cache) */
|
|
868
718
|
refetch: () => void;
|
|
869
719
|
}
|
|
@@ -887,6 +737,8 @@ interface UsePollingResult<T> {
|
|
|
887
737
|
isRunning: boolean;
|
|
888
738
|
stop: () => void;
|
|
889
739
|
start: () => void;
|
|
740
|
+
/** Immediately trigger a fetch without waiting for the next interval */
|
|
741
|
+
refetch: () => void;
|
|
890
742
|
}
|
|
891
743
|
declare function usePolling<T>(fetcher: () => Promise<T>, options?: UsePollingOptions<T>): UsePollingResult<T>;
|
|
892
744
|
|
|
@@ -895,4 +747,4 @@ declare function formatNumber(n: number): string;
|
|
|
895
747
|
declare function formatPrice(amount: number, currency?: string, locale?: string): string;
|
|
896
748
|
declare function cn(...classes: (string | undefined | null | false | 0)[]): string;
|
|
897
749
|
|
|
898
|
-
export { type ApiClient, type ApiClientOptions, ApiError, AppShell, AppShellContent, type AppShellContentProps, AppShellHeader, type AppShellHeaderProps, type AppShellProps, Avatar, type AvatarProps, Badge, type BadgeProps, Button, type ButtonProps, type ColorName, Dialog, type DialogProps, Divider, EmojiButton, type EmojiButtonProps, EmojiPicker, type EmojiPickerProps,
|
|
750
|
+
export { type ApiClient, type ApiClientOptions, ApiError, AppShell, AppShellContent, type AppShellContentProps, AppShellHeader, type AppShellHeaderProps, type AppShellProps, Avatar, type AvatarProps, Badge, type BadgeProps, Button, type ButtonProps, type ColorName, Dialog, type DialogProps, Divider, EmojiButton, type EmojiButtonProps, EmojiPicker, type EmojiPickerProps, EmptyState, type EmptyStateProps, type FontName, GrassMap, type GrassMapData, type GrassMapProps, IOSInstallSheet, PWAInstallButton, type PWAInstallState, Section, SectionHeader, type SectionHeaderProps, type SectionProps, ShareButton, type ShareButtonProps, Skeleton, type SkeletonProps, StatChip, type StatChipProps, THEME_SCRIPT, Tab, TabBar, type TabBarProps, type TabProps, ThemeButton, type ThemeButtonProps, ThemeDialog, type ThemeDialogProps, type ToastOptions, ToastProvider, type ToastVariant, Tooltip, type TooltipProps, Typewriter, type TypewriterProps, type UseFetchOptions, type UseFetchResult, type UseFormSubmitOptions, type UseFormSubmitResult, type UseInViewOptions, type UseInViewResult, type UsePWAInstallReturn, type UsePollingOptions, type UsePollingResult, type UseShareOptions, type UseShareReturn, Watermark, type WatermarkProps, type WatermarkSponsor, clearFetchCache, cn, colors, createApiClient, createManifest, fontFamily, fonts, formatNumber, formatPrice, mobileViewport, relativeTime, svgIcon, useDebounce, useFetch, useFormSubmit, useInView, useLocalStorage, usePWAInstall, usePolling, useShare, useToast };
|
package/dist/index.d.ts
CHANGED
|
@@ -438,8 +438,14 @@ interface UseFormSubmitOptions<T> {
|
|
|
438
438
|
onSuccess?: (data: T) => void;
|
|
439
439
|
onError?: (err: Error) => void;
|
|
440
440
|
}
|
|
441
|
+
/**
|
|
442
|
+
* Submit function type:
|
|
443
|
+
* - When V is `void` (fn takes no arguments) → `submit()` needs no args
|
|
444
|
+
* - Otherwise → `submit(vars: V)` requires the arg
|
|
445
|
+
*/
|
|
446
|
+
type SubmitFn<V> = V extends void ? () => Promise<void> : (vars: V) => Promise<void>;
|
|
441
447
|
interface UseFormSubmitResult<T, V> {
|
|
442
|
-
submit:
|
|
448
|
+
submit: SubmitFn<V>;
|
|
443
449
|
loading: boolean;
|
|
444
450
|
error: Error | null;
|
|
445
451
|
data: T | undefined;
|
|
@@ -450,16 +456,21 @@ interface UseFormSubmitResult<T, V> {
|
|
|
450
456
|
* Eliminates the try/catch/finally + setState boilerplate from every form.
|
|
451
457
|
*
|
|
452
458
|
* @example
|
|
459
|
+
* // With args
|
|
453
460
|
* const { submit, loading, error } = useFormSubmit(async (url: string) => {
|
|
454
461
|
* return api.post<Site>("/api/sites", { url });
|
|
455
462
|
* }, {
|
|
456
|
-
* onSuccess: () => router.push(
|
|
463
|
+
* onSuccess: (site) => router.push(`/${site.slug}`),
|
|
457
464
|
* });
|
|
465
|
+
* <button onClick={() => submit(inputValue)}>등록</button>
|
|
458
466
|
*
|
|
459
|
-
*
|
|
460
|
-
*
|
|
461
|
-
*
|
|
462
|
-
*
|
|
467
|
+
* @example
|
|
468
|
+
* // No args (fire-and-forget style)
|
|
469
|
+
* const { submit: save, loading } = useFormSubmit(
|
|
470
|
+
* () => api.put("/api/sites/settings", config),
|
|
471
|
+
* { onSuccess: () => setSaved(true) }
|
|
472
|
+
* );
|
|
473
|
+
* <button onClick={save}>저장</button>
|
|
463
474
|
*/
|
|
464
475
|
declare function useFormSubmit<T, V = void>(fn: (vars: V) => Promise<T>, options?: UseFormSubmitOptions<T>): UseFormSubmitResult<T, V>;
|
|
465
476
|
|
|
@@ -542,178 +553,6 @@ interface DialogProps {
|
|
|
542
553
|
*/
|
|
543
554
|
declare function Dialog({ open, onClose, title, size, children, persistent, className, }: DialogProps): React__default.ReactPortal | null;
|
|
544
555
|
|
|
545
|
-
interface OGConfig {
|
|
546
|
-
/** 앱 이름 (좌상단 로고) */
|
|
547
|
-
appName?: string;
|
|
548
|
-
/** 브랜드 색상 (hex) */
|
|
549
|
-
color?: string;
|
|
550
|
-
/** 하단 도메인 */
|
|
551
|
-
domain?: string;
|
|
552
|
-
/** 배경 스타일 — "dark"(기본) | "gradient"(다크 오로라) | "blend"(라이트 파스텔 블렌드) */
|
|
553
|
-
bg?: "dark" | "gradient" | "blend";
|
|
554
|
-
/** 로고 이미지 URL (지정 시 appName 첫 글자 대신 이미지 표시) */
|
|
555
|
-
logoUrl?: string;
|
|
556
|
-
}
|
|
557
|
-
interface OGDefaultTemplate {
|
|
558
|
-
type?: "default";
|
|
559
|
-
title: string;
|
|
560
|
-
sub?: string;
|
|
561
|
-
badge?: string;
|
|
562
|
-
}
|
|
563
|
-
interface OGMatchTemplate {
|
|
564
|
-
type: "match";
|
|
565
|
-
home: string;
|
|
566
|
-
away: string;
|
|
567
|
-
score?: string;
|
|
568
|
-
sub?: string;
|
|
569
|
-
badge?: string;
|
|
570
|
-
}
|
|
571
|
-
interface OGSquareTemplate {
|
|
572
|
-
type: "square";
|
|
573
|
-
title: string;
|
|
574
|
-
sub?: string;
|
|
575
|
-
badge?: string;
|
|
576
|
-
}
|
|
577
|
-
interface OGIconTemplate {
|
|
578
|
-
type: "icon";
|
|
579
|
-
/** 아이콘에 표시할 글자 (기본: appName 첫 글자) */
|
|
580
|
-
letter?: string;
|
|
581
|
-
/** 아이콘 모서리 둥글기 (기본: 96) */
|
|
582
|
-
radius?: number;
|
|
583
|
-
}
|
|
584
|
-
interface OGArticleTemplate {
|
|
585
|
-
type: "article";
|
|
586
|
-
title: string;
|
|
587
|
-
/** 작성자 */
|
|
588
|
-
author?: string;
|
|
589
|
-
/** 날짜 또는 발행일 */
|
|
590
|
-
date?: string;
|
|
591
|
-
/** 카테고리 / 태그 */
|
|
592
|
-
category?: string;
|
|
593
|
-
sub?: string;
|
|
594
|
-
}
|
|
595
|
-
interface OGStatTemplate {
|
|
596
|
-
type: "stat";
|
|
597
|
-
/** 강조할 숫자 또는 지표 */
|
|
598
|
-
stat: string;
|
|
599
|
-
/** 지표 설명 */
|
|
600
|
-
label: string;
|
|
601
|
-
sub?: string;
|
|
602
|
-
badge?: string;
|
|
603
|
-
}
|
|
604
|
-
interface OGProductTemplate {
|
|
605
|
-
type: "product";
|
|
606
|
-
title: string;
|
|
607
|
-
tagline?: string;
|
|
608
|
-
/** 핵심 특징 (최대 3개) */
|
|
609
|
-
features?: string[];
|
|
610
|
-
badge?: string;
|
|
611
|
-
}
|
|
612
|
-
type OGTemplate = OGDefaultTemplate | OGMatchTemplate | OGSquareTemplate | OGIconTemplate | OGArticleTemplate | OGStatTemplate | OGProductTemplate;
|
|
613
|
-
type OGProps = OGTemplate & OGConfig;
|
|
614
|
-
declare function OGImage(props: OGProps): react_jsx_runtime.JSX.Element;
|
|
615
|
-
|
|
616
|
-
/** @vercel/og (Satori) fonts 옵션에 넘길 수 있는 폰트 정의 */
|
|
617
|
-
interface OGFont {
|
|
618
|
-
name: string;
|
|
619
|
-
data: ArrayBuffer;
|
|
620
|
-
weight: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;
|
|
621
|
-
style?: "normal" | "italic";
|
|
622
|
-
}
|
|
623
|
-
declare const PRETENDARD_WEIGHTS: {
|
|
624
|
-
readonly 400: "Pretendard-Regular.otf";
|
|
625
|
-
readonly 700: "Pretendard-Bold.otf";
|
|
626
|
-
readonly 900: "Pretendard-Black.otf";
|
|
627
|
-
};
|
|
628
|
-
type PretendardWeight = keyof typeof PRETENDARD_WEIGHTS;
|
|
629
|
-
/**
|
|
630
|
-
* Pretendard 폰트를 CDN에서 로드합니다.
|
|
631
|
-
*
|
|
632
|
-
* @example
|
|
633
|
-
* ```ts
|
|
634
|
-
* import { OG, loadPretendard } from "@m1kapp/seo";
|
|
635
|
-
* import { ImageResponse } from "next/og";
|
|
636
|
-
*
|
|
637
|
-
* export async function GET() {
|
|
638
|
-
* const fonts = await loadPretendard();
|
|
639
|
-
* return new ImageResponse(<OG type="default" title="Hello" />, {
|
|
640
|
-
* width: 1200, height: 630, fonts,
|
|
641
|
-
* });
|
|
642
|
-
* }
|
|
643
|
-
* ```
|
|
644
|
-
*
|
|
645
|
-
* @param weights 로드할 weight 배열 (기본: [700, 900])
|
|
646
|
-
*/
|
|
647
|
-
declare function loadPretendard(weights?: PretendardWeight[]): Promise<OGFont[]>;
|
|
648
|
-
/**
|
|
649
|
-
* 임의의 URL에서 폰트를 로드합니다.
|
|
650
|
-
*
|
|
651
|
-
* @example
|
|
652
|
-
* ```ts
|
|
653
|
-
* const fonts = await loadFont({
|
|
654
|
-
* name: "CustomFont",
|
|
655
|
-
* url: "https://example.com/CustomFont-Bold.otf",
|
|
656
|
-
* weight: 700,
|
|
657
|
-
* });
|
|
658
|
-
* ```
|
|
659
|
-
*/
|
|
660
|
-
declare function loadFont(opts: {
|
|
661
|
-
name: string;
|
|
662
|
-
url: string;
|
|
663
|
-
weight?: OGFont["weight"];
|
|
664
|
-
style?: OGFont["style"];
|
|
665
|
-
}): Promise<OGFont>;
|
|
666
|
-
/**
|
|
667
|
-
* Google Fonts에서 폰트를 로드합니다.
|
|
668
|
-
* CSS 응답을 파싱해 TTF/OTF URL을 자동 추출합니다.
|
|
669
|
-
*
|
|
670
|
-
* @example
|
|
671
|
-
* ```ts
|
|
672
|
-
* const fonts = await loadGoogleFont("Noto Sans KR", [400, 700]);
|
|
673
|
-
* return new ImageResponse(<OG ... />, { fonts });
|
|
674
|
-
* ```
|
|
675
|
-
*/
|
|
676
|
-
declare function loadGoogleFont(family: string, weights?: OGFont["weight"][]): Promise<OGFont[]>;
|
|
677
|
-
|
|
678
|
-
/**
|
|
679
|
-
* @vercel/og ImageResponse에서 지원하는 이모지 스타일.
|
|
680
|
-
* ImageResponse 옵션의 `emoji` 필드에 넘기면 됩니다.
|
|
681
|
-
*/
|
|
682
|
-
type EmojiStyle = "twemoji" | "openmoji" | "noto" | "fluent" | "fluentFlat" | "blobmoji";
|
|
683
|
-
/**
|
|
684
|
-
* raw Satori의 `loadAdditionalAsset` 콜백을 생성합니다.
|
|
685
|
-
*
|
|
686
|
-
* @example
|
|
687
|
-
* ```ts
|
|
688
|
-
* import satori from "satori";
|
|
689
|
-
* import { createEmojiLoader } from "@m1kapp/seo";
|
|
690
|
-
*
|
|
691
|
-
* const svg = await satori(<OG ... />, {
|
|
692
|
-
* width: 1200, height: 630,
|
|
693
|
-
* fonts: [...],
|
|
694
|
-
* loadAdditionalAsset: createEmojiLoader(),
|
|
695
|
-
* });
|
|
696
|
-
* ```
|
|
697
|
-
*/
|
|
698
|
-
declare function createEmojiLoader(): (code: string, segment: string) => Promise<string | undefined>;
|
|
699
|
-
|
|
700
|
-
/**
|
|
701
|
-
* OG 이미지 URL의 캐시를 즉시 무효화할 버전 키를 반환합니다.
|
|
702
|
-
*
|
|
703
|
-
* Cache-Control로 CDN에 캐싱된 OG 이미지를 강제 갱신하고 싶을 때
|
|
704
|
-
* URL의 `v` 파라미터에 이 값을 넣으세요.
|
|
705
|
-
*
|
|
706
|
-
* @example
|
|
707
|
-
* ```ts
|
|
708
|
-
* import { getOGVersion } from "@m1kapp/seo";
|
|
709
|
-
*
|
|
710
|
-
* // OG 갱신 버튼 핸들러
|
|
711
|
-
* const freshUrl = `/og?title=${title}&v=${getOGVersion()}`;
|
|
712
|
-
* // → /og?title=...&v=20260415152347
|
|
713
|
-
* ```
|
|
714
|
-
*/
|
|
715
|
-
declare function getOGVersion(): string;
|
|
716
|
-
|
|
717
556
|
type PWAInstallState = "android-ready" | "ios-safari" | "installed" | "unsupported";
|
|
718
557
|
interface UsePWAInstallReturn {
|
|
719
558
|
state: PWAInstallState;
|
|
@@ -816,7 +655,9 @@ declare class ApiError extends Error {
|
|
|
816
655
|
readonly status: number;
|
|
817
656
|
readonly statusText: string;
|
|
818
657
|
readonly body: unknown;
|
|
819
|
-
|
|
658
|
+
readonly url?: string | undefined;
|
|
659
|
+
readonly method?: string | undefined;
|
|
660
|
+
constructor(status: number, statusText: string, body: unknown, url?: string | undefined, method?: string | undefined);
|
|
820
661
|
}
|
|
821
662
|
|
|
822
663
|
interface ApiClientOptions {
|
|
@@ -824,7 +665,7 @@ interface ApiClientOptions {
|
|
|
824
665
|
headers?: Record<string, string>;
|
|
825
666
|
/** Called before every request — mutate or replace the Request */
|
|
826
667
|
onRequest?: (req: Request) => Request | void;
|
|
827
|
-
/** Called on every non-2xx response */
|
|
668
|
+
/** Called on every non-2xx response — includes url and method for debugging */
|
|
828
669
|
onError?: (err: ApiError) => void;
|
|
829
670
|
}
|
|
830
671
|
type RequestOptions = {
|
|
@@ -844,6 +685,7 @@ type ApiClient = ReturnType<typeof createApiClient>;
|
|
|
844
685
|
|
|
845
686
|
/** Manually invalidate cache entries. Pass a URL to clear one, or nothing to clear all. */
|
|
846
687
|
declare function clearFetchCache(url?: string): void;
|
|
688
|
+
type FetchStatus = "idle" | "loading" | "success" | "error";
|
|
847
689
|
interface UseFetchOptions<T> {
|
|
848
690
|
/** Skip fetching when false (default: true) */
|
|
849
691
|
enabled?: boolean;
|
|
@@ -864,6 +706,14 @@ interface UseFetchResult<T> {
|
|
|
864
706
|
data: T | undefined;
|
|
865
707
|
loading: boolean;
|
|
866
708
|
error: Error | undefined;
|
|
709
|
+
/**
|
|
710
|
+
* Lifecycle status:
|
|
711
|
+
* - `"idle"` — url is null/undefined or `enabled: false`
|
|
712
|
+
* - `"loading"` — fetch in progress
|
|
713
|
+
* - `"success"` — data loaded (note: data may be null if the API returned null)
|
|
714
|
+
* - `"error"` — last fetch failed
|
|
715
|
+
*/
|
|
716
|
+
status: FetchStatus;
|
|
867
717
|
/** Manually trigger a refetch (ignores cache) */
|
|
868
718
|
refetch: () => void;
|
|
869
719
|
}
|
|
@@ -887,6 +737,8 @@ interface UsePollingResult<T> {
|
|
|
887
737
|
isRunning: boolean;
|
|
888
738
|
stop: () => void;
|
|
889
739
|
start: () => void;
|
|
740
|
+
/** Immediately trigger a fetch without waiting for the next interval */
|
|
741
|
+
refetch: () => void;
|
|
890
742
|
}
|
|
891
743
|
declare function usePolling<T>(fetcher: () => Promise<T>, options?: UsePollingOptions<T>): UsePollingResult<T>;
|
|
892
744
|
|
|
@@ -895,4 +747,4 @@ declare function formatNumber(n: number): string;
|
|
|
895
747
|
declare function formatPrice(amount: number, currency?: string, locale?: string): string;
|
|
896
748
|
declare function cn(...classes: (string | undefined | null | false | 0)[]): string;
|
|
897
749
|
|
|
898
|
-
export { type ApiClient, type ApiClientOptions, ApiError, AppShell, AppShellContent, type AppShellContentProps, AppShellHeader, type AppShellHeaderProps, type AppShellProps, Avatar, type AvatarProps, Badge, type BadgeProps, Button, type ButtonProps, type ColorName, Dialog, type DialogProps, Divider, EmojiButton, type EmojiButtonProps, EmojiPicker, type EmojiPickerProps,
|
|
750
|
+
export { type ApiClient, type ApiClientOptions, ApiError, AppShell, AppShellContent, type AppShellContentProps, AppShellHeader, type AppShellHeaderProps, type AppShellProps, Avatar, type AvatarProps, Badge, type BadgeProps, Button, type ButtonProps, type ColorName, Dialog, type DialogProps, Divider, EmojiButton, type EmojiButtonProps, EmojiPicker, type EmojiPickerProps, EmptyState, type EmptyStateProps, type FontName, GrassMap, type GrassMapData, type GrassMapProps, IOSInstallSheet, PWAInstallButton, type PWAInstallState, Section, SectionHeader, type SectionHeaderProps, type SectionProps, ShareButton, type ShareButtonProps, Skeleton, type SkeletonProps, StatChip, type StatChipProps, THEME_SCRIPT, Tab, TabBar, type TabBarProps, type TabProps, ThemeButton, type ThemeButtonProps, ThemeDialog, type ThemeDialogProps, type ToastOptions, ToastProvider, type ToastVariant, Tooltip, type TooltipProps, Typewriter, type TypewriterProps, type UseFetchOptions, type UseFetchResult, type UseFormSubmitOptions, type UseFormSubmitResult, type UseInViewOptions, type UseInViewResult, type UsePWAInstallReturn, type UsePollingOptions, type UsePollingResult, type UseShareOptions, type UseShareReturn, Watermark, type WatermarkProps, type WatermarkSponsor, clearFetchCache, cn, colors, createApiClient, createManifest, fontFamily, fonts, formatNumber, formatPrice, mobileViewport, relativeTime, svgIcon, useDebounce, useFetch, useFormSubmit, useInView, useLocalStorage, usePWAInstall, usePolling, useShare, useToast };
|