@musecat/functionkit 1.0.1 → 1.1.0

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.
Files changed (130) hide show
  1. package/.agents/references/components/ScrolltoTop.md +36 -0
  2. package/.agents/references/components/SwitchCase.md +47 -0
  3. package/.agents/references/components/ViewportPortal.md +34 -0
  4. package/.agents/references/cookie/cookie.md +47 -0
  5. package/.agents/references/datetime/dateTime.client.md +42 -0
  6. package/.agents/references/datetime/dateTime.server.md +42 -0
  7. package/.agents/references/datetime/dateTime.shared.md +86 -0
  8. package/.agents/references/hooks/useAvoidKeyboard.md +29 -0
  9. package/.agents/references/hooks/useCheckInvisible.md +36 -0
  10. package/.agents/references/hooks/useCheckScroll.md +36 -0
  11. package/.agents/references/hooks/useClientDateTime.md +39 -0
  12. package/.agents/references/hooks/useDebounce.md +46 -0
  13. package/.agents/references/hooks/useDebouncedCallback.md +34 -0
  14. package/.agents/references/hooks/useDoubleClick.md +40 -0
  15. package/.agents/references/hooks/useGeolocation.md +37 -0
  16. package/.agents/references/hooks/useHasMounted.md +27 -0
  17. package/.agents/references/hooks/useIntersectionObserver.md +41 -0
  18. package/.agents/references/hooks/useInterval.md +44 -0
  19. package/.agents/references/hooks/useKeyboardHeight.md +33 -0
  20. package/.agents/references/hooks/useKeyboardListNavigation.md +58 -0
  21. package/.agents/references/hooks/useLongPress.md +48 -0
  22. package/.agents/references/hooks/usePreservedCallback.md +37 -0
  23. package/.agents/references/hooks/usePreservedReference.md +34 -0
  24. package/.agents/references/hooks/useRefEffect.md +35 -0
  25. package/.agents/references/hooks/useRelativeDateTime.md +35 -0
  26. package/.agents/references/hooks/useTimeout.md +42 -0
  27. package/.agents/references/hooks/useToggleState.md +39 -0
  28. package/.agents/references/hooks/useViewportHeight.md +26 -0
  29. package/.agents/references/hooks/useViewportMatch.md +32 -0
  30. package/.agents/references/utils/browserStorage.md +41 -0
  31. package/.agents/references/utils/buildContext.md +47 -0
  32. package/.agents/references/utils/clipboardShare.md +59 -0
  33. package/.agents/references/utils/floatingMotion.md +67 -0
  34. package/.agents/references/utils/getDeviceInfo.md +43 -0
  35. package/.agents/references/utils/isEditableKeyboardTarget.md +35 -0
  36. package/.agents/references/utils/mergeRefs.md +29 -0
  37. package/.agents/references/utils/seen.md +37 -0
  38. package/.agents/references/utils/subscribeKeyboardHeight.md +35 -0
  39. package/.github/ISSUE_TEMPLATE/bug_report.md +31 -0
  40. package/.github/ISSUE_TEMPLATE/feature_request.md +22 -0
  41. package/.github/PULL_REQUEST_TEMPLATE.md +22 -0
  42. package/.github/workflows/ci.yml +20 -0
  43. package/.local/state/gh/device-id +1 -0
  44. package/.prettierrc +8 -0
  45. package/AGENTS.md +117 -396
  46. package/CODE_OF_CONDUCT.md +55 -0
  47. package/CONTRIBUTING.md +39 -0
  48. package/README.md +18 -5
  49. package/biome.json +25 -0
  50. package/index.ts +63 -68
  51. package/package.json +46 -13
  52. package/packages/components/ScrolltoTop.tsx +3 -3
  53. package/packages/components/SwitchCase.tsx +7 -7
  54. package/packages/components/ViewportPortal.tsx +22 -23
  55. package/packages/cookie/cookie.shared.ts +80 -110
  56. package/packages/cookie/cookieNames.shared.ts +9 -0
  57. package/packages/datetime/dateTime.client.ts +84 -86
  58. package/packages/datetime/dateTime.server.ts +84 -86
  59. package/packages/datetime/dateTime.shared.ts +249 -287
  60. package/packages/hooks/useAvoidKeyboard.ts +15 -15
  61. package/packages/hooks/useCheckInvisible.ts +16 -16
  62. package/packages/hooks/useCheckScroll.ts +10 -10
  63. package/packages/hooks/useClientDateTime.ts +24 -37
  64. package/packages/hooks/useDebounce.ts +90 -95
  65. package/packages/hooks/useDebouncedCallback.ts +45 -45
  66. package/packages/hooks/useDoubleClick.ts +43 -43
  67. package/packages/hooks/useGeolocation.ts +126 -130
  68. package/packages/hooks/useHasMounted.ts +5 -5
  69. package/packages/hooks/useIntersectionObserver.ts +20 -20
  70. package/packages/hooks/useInterval.ts +45 -45
  71. package/packages/hooks/useKeyboardHeight.ts +9 -9
  72. package/packages/hooks/useKeyboardListNavigation.ts +130 -158
  73. package/packages/hooks/useLongPress.ts +87 -98
  74. package/packages/hooks/usePreservedCallback.ts +12 -12
  75. package/packages/hooks/usePreservedReference.ts +10 -10
  76. package/packages/hooks/useRefEffect.ts +19 -19
  77. package/packages/hooks/useRelativeDateTime.ts +36 -39
  78. package/packages/hooks/useTimeout.ts +41 -41
  79. package/packages/hooks/useToggleState.ts +5 -5
  80. package/packages/hooks/useViewportHeight.ts +12 -13
  81. package/packages/hooks/useViewportMatch.ts +13 -23
  82. package/packages/utils/browserStorage.ts +26 -26
  83. package/packages/utils/buildContext.tsx +10 -11
  84. package/packages/utils/checkDevice.ts +64 -64
  85. package/packages/utils/clipboardShare.tsx +31 -31
  86. package/packages/utils/clipboardShare.types.ts +7 -7
  87. package/packages/utils/floatingMotion.ts +71 -77
  88. package/packages/utils/keyboardTarget.ts +9 -9
  89. package/packages/utils/mergeRefs.ts +9 -9
  90. package/packages/utils/seen.ts +15 -15
  91. package/packages/utils/subscribeKeyboardHeight.ts +49 -46
  92. package/tests/components/ScrolltoTop.test.tsx +15 -0
  93. package/tests/components/SwitchCase.test.tsx +33 -0
  94. package/tests/components/ViewportPortal.test.tsx +46 -0
  95. package/tests/cookie/cookie.test.ts +192 -0
  96. package/tests/datetime/datetime.test.ts +461 -0
  97. package/tests/hooks/useAvoidKeyboard.test.ts +43 -0
  98. package/tests/hooks/useCheckInvisible.test.ts +59 -0
  99. package/tests/hooks/useCheckScroll.test.ts +23 -0
  100. package/tests/hooks/useClientDateTime.test.ts +12 -0
  101. package/tests/hooks/useDebounce.test.ts +96 -0
  102. package/tests/hooks/useDebouncedCallback.test.ts +63 -0
  103. package/tests/hooks/useDoubleClick.test.ts +82 -0
  104. package/tests/hooks/useGeolocation.test.ts +201 -0
  105. package/tests/hooks/useHasMounted.test.ts +10 -0
  106. package/tests/hooks/useIntersectionObserver.test.ts +78 -0
  107. package/tests/hooks/useInterval.test.ts +56 -0
  108. package/tests/hooks/useKeyboardHeight.test.ts +10 -0
  109. package/tests/hooks/useKeyboardListNavigation.test.ts +404 -0
  110. package/tests/hooks/useLongPress.test.ts +214 -0
  111. package/tests/hooks/usePreservedCallback.test.ts +33 -0
  112. package/tests/hooks/usePreservedReference.test.ts +42 -0
  113. package/tests/hooks/useRefEffect.test.ts +67 -0
  114. package/tests/hooks/useRelativeDateTime.test.ts +21 -0
  115. package/tests/hooks/useTimeout.test.ts +72 -0
  116. package/tests/hooks/useToggleState.test.ts +43 -0
  117. package/tests/hooks/useViewportHeight.test.ts +32 -0
  118. package/tests/hooks/useViewportMatch.test.ts +35 -0
  119. package/tests/setup.ts +9 -0
  120. package/tests/utils/browserStorage.test.ts +114 -0
  121. package/tests/utils/buildContext.test.tsx +26 -0
  122. package/tests/utils/checkDevice.test.ts +140 -0
  123. package/tests/utils/clipboardShare.test.ts +152 -0
  124. package/tests/utils/floatingMotion.test.ts +135 -0
  125. package/tests/utils/keyboardTarget.test.ts +85 -0
  126. package/tests/utils/mergeRefs.test.ts +50 -0
  127. package/tests/utils/seen.test.ts +40 -0
  128. package/tests/utils/subscribeKeyboardHeight.test.ts +172 -0
  129. package/tsconfig.json +16 -16
  130. package/vitest.config.ts +22 -0
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @musecat/functionkit
2
2
 
3
- React 19 기반 프론트엔드 유틸리티 라이브러리입니다.
3
+ A React 19 + TypeScript UI F.E. utility library.
4
4
 
5
5
  ## Installation
6
6
 
@@ -10,16 +10,29 @@ npm install @musecat/functionkit
10
10
 
11
11
  ## Usage
12
12
 
13
- 모든 훅, 컴포넌트, 유틸리티는 배럴 파일을 통해 직접 임포트하여 사용할 수 있습니다.
14
-
15
13
  ```tsx
16
14
  import { useDebounce, formatClientDateTime } from "@musecat/functionkit";
15
+
16
+ export default function Example() {
17
+ const handleSearch = useDebounce((term: string) => fetchResults(term), 300);
18
+ const now = formatClientDateTime(new Date(), {
19
+ datePreset: "long",
20
+ timePreset: "24h-minute",
21
+ });
22
+
23
+ return (
24
+ <View column gap={24}>
25
+ <Input onChange={(e) => handleSearch(e.target.value)} />
26
+ <Text type="Caption1">{now}</Text>
27
+ </View>
28
+ );
29
+ }
17
30
  ```
18
31
 
19
32
  ## Acknowledgements
20
33
 
21
- 프로젝트의 많은 유틸리티 및 훅 코드는 [toss/react-simplikit](https://github.com/toss/react-simplikit/) 프로젝트를 참조 및 영감을 받아 작성되었습니다.
34
+ - [toss/react-simplikit](https://github.com/toss/react-simplikit/)
22
35
 
23
36
  ## License
24
37
 
25
- MIT License © Musecat Team. 여기에서 [라이센스](./LICENSE) 정보 확인하기
38
+ [MIT License](./LICENSE) © Musecat Team.
package/biome.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "$schema": "https://biomejs.dev/schemas/2.5.4/schema.json",
3
+ "assist": { "actions": { "source": { "organizeImports": "on" } } },
4
+ "linter": {
5
+ "enabled": true,
6
+ "rules": {
7
+ "preset": "recommended"
8
+ }
9
+ },
10
+ "formatter": {
11
+ "enabled": true,
12
+ "indentStyle": "tab",
13
+ "indentWidth": 2,
14
+ "lineWidth": 100
15
+ },
16
+ "javascript": {
17
+ "formatter": {
18
+ "quoteStyle": "double",
19
+ "semicolons": "always"
20
+ }
21
+ },
22
+ "files": {
23
+ "includes": ["**", "!**/dist", "!**/coverage", "!**/node_modules"]
24
+ }
25
+ }
package/index.ts CHANGED
@@ -1,53 +1,49 @@
1
- // 컴포넌트
2
1
  export {
3
- getViewportPortalRoot,
4
- ViewportPortal
2
+ getViewportPortalRoot,
3
+ ViewportPortal,
5
4
  } from "./packages/components/ViewportPortal";
6
- // 쿠키
7
5
  export {
8
- clearAllClientCookies,
9
- clearClientCookie,
10
- getClientCookie,
11
- parseClientCookieNames,
12
- setClientCookie
6
+ clearAllClientCookies,
7
+ clearClientCookie,
8
+ getClientCookie,
9
+ setClientCookie,
13
10
  } from "./packages/cookie/cookie.shared";
14
- // 날짜/시간
11
+ export { parseClientCookieNames } from "./packages/cookie/cookieNames.shared";
15
12
  export {
16
- formatClientDate,
17
- formatClientDateTime,
18
- formatClientRelative,
19
- formatClientTime
13
+ formatClientDate,
14
+ formatClientDateTime,
15
+ formatClientRelative,
16
+ formatClientTime,
20
17
  } from "./packages/datetime/dateTime.client";
21
18
  export {
22
- formatServerDate,
23
- formatServerDateTime,
24
- formatServerRelative,
25
- formatServerTime
19
+ formatServerDate,
20
+ formatServerDateTime,
21
+ formatServerRelative,
22
+ formatServerTime,
26
23
  } from "./packages/datetime/dateTime.server";
27
- export {
28
- addUtcDays,
29
- format24HourTime,
30
- formatDotDate,
31
- formatKoreanTime,
32
- formatLongDate,
33
- formatRelativeText,
34
- formatRemainingText,
35
- formatTwelveHourTime,
36
- formatUtcDateKey,
37
- getUtcWeekdayIndex,
38
- normalizeAppLocale,
39
- parseUtcDateInput,
40
- toDate,
41
- toIntlLocale,
42
- toUtcMidnight
43
- } from "./packages/datetime/dateTime.shared";
44
24
  export type {
45
- AppLocale,
46
- DateInput,
47
- DatePreset,
48
- TimePreset
25
+ AppLocale,
26
+ DateInput,
27
+ DatePreset,
28
+ TimePreset,
29
+ } from "./packages/datetime/dateTime.shared";
30
+ export {
31
+ addUtcDays,
32
+ format24HourTime,
33
+ formatDotDate,
34
+ formatKoreanTime,
35
+ formatLongDate,
36
+ formatRelativeText,
37
+ formatRemainingText,
38
+ formatTwelveHourTime,
39
+ formatUtcDateKey,
40
+ getUtcWeekdayIndex,
41
+ normalizeAppLocale,
42
+ parseUtcDateInput,
43
+ toDate,
44
+ toIntlLocale,
45
+ toUtcMidnight,
49
46
  } from "./packages/datetime/dateTime.shared";
50
- // hooks
51
47
  export { useAvoidKeyboard } from "./packages/hooks/useAvoidKeyboard";
52
48
  export { useClientDateTime } from "./packages/hooks/useClientDateTime";
53
49
  export { useDebounce } from "./packages/hooks/useDebounce";
@@ -68,44 +64,43 @@ export { useTimeout } from "./packages/hooks/useTimeout";
68
64
  export { useToggleState } from "./packages/hooks/useToggleState";
69
65
  export { useViewportHeight } from "./packages/hooks/useViewportHeight";
70
66
  export { useViewportMatch } from "./packages/hooks/useViewportMatch";
71
- // 유틸리티
72
67
  export {
73
- getLocalStorage,
74
- getSessionStorage,
75
- removeLocalStorage,
76
- removeSessionStorage,
77
- updateLocalStorage,
78
- updateSessionStorage
68
+ getLocalStorage,
69
+ getSessionStorage,
70
+ removeLocalStorage,
71
+ removeSessionStorage,
72
+ updateLocalStorage,
73
+ updateSessionStorage,
79
74
  } from "./packages/utils/browserStorage";
80
- // 유틸리티
81
75
  export { buildContext } from "./packages/utils/buildContext";
82
- export { getDeviceInfo } from "./packages/utils/checkDevice";
83
76
  export type { DeviceInfo } from "./packages/utils/checkDevice";
84
- export { NavigatorClipboard, NavigatorShare } from "./packages/utils/clipboardShare";
77
+ export { getDeviceInfo } from "./packages/utils/checkDevice";
78
+ export {
79
+ NavigatorClipboard,
80
+ NavigatorShare,
81
+ } from "./packages/utils/clipboardShare";
85
82
  export type {
86
- NavigatorClipboardProps,
87
- NavigatorClipboardResult,
88
- NavigatorShareProps,
89
- NavigatorShareResult
83
+ NavigatorClipboardProps,
84
+ NavigatorClipboardResult,
85
+ NavigatorShareProps,
86
+ NavigatorShareResult,
90
87
  } from "./packages/utils/clipboardShare.types";
91
- export {
92
- getFloatingHiddenTransform,
93
- getFloatingMotionPreset,
94
- getFloatingTransformOrigin
95
- } from "./packages/utils/floatingMotion";
96
88
  export type {
97
- FloatingMotionMode,
98
- FloatingMotionPreset,
99
- FloatingPlacement
89
+ FloatingMotionMode,
90
+ FloatingMotionPreset,
91
+ FloatingPlacement,
92
+ } from "./packages/utils/floatingMotion";
93
+ export {
94
+ getFloatingHiddenTransform,
95
+ getFloatingMotionPreset,
96
+ getFloatingTransformOrigin,
100
97
  } from "./packages/utils/floatingMotion";
101
- // 유틸리티
102
98
  export { isEditableKeyboardTarget } from "./packages/utils/keyboardTarget";
103
99
  export { mergeRefs } from "./packages/utils/mergeRefs";
104
100
  export {
105
- buildSeenValue,
106
- hasSeenKey,
107
- parseSeen,
108
- SEEN_STORAGE_KEY
101
+ buildSeenValue,
102
+ hasSeenKey,
103
+ parseSeen,
104
+ SEEN_STORAGE_KEY,
109
105
  } from "./packages/utils/seen";
110
106
  export { subscribeKeyboardHeight } from "./packages/utils/subscribeKeyboardHeight";
111
-
package/package.json CHANGED
@@ -1,15 +1,48 @@
1
1
  {
2
- "name": "@musecat/functionkit",
3
- "version": "1.0.1",
4
- "type": "module",
5
- "main": "./index.ts",
6
- "types": "./index.ts",
7
- "exports": {
8
- ".": "./index.ts",
9
- "./*": "./*"
10
- },
11
- "peerDependencies": {
12
- "react": "^19.0.0",
13
- "react-dom": "^19.0.0"
14
- }
2
+ "name": "@musecat/functionkit",
3
+ "version": "1.1.0",
4
+ "description": "React 19 + TypeScript frontend utility library — hooks, components, datetime, cookie, and utils",
5
+ "keywords": [
6
+ "react",
7
+ "hooks",
8
+ "typescript",
9
+ "utility",
10
+ "frontend"
11
+ ],
12
+ "license": "MIT",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/TheTechclip/FunctionKit.git"
16
+ },
17
+ "bugs": {
18
+ "url": "https://github.com/TheTechclip/FunctionKit/issues"
19
+ },
20
+ "homepage": "https://musecat.app",
21
+ "type": "module",
22
+ "main": "./index.ts",
23
+ "types": "./index.ts",
24
+ "exports": {
25
+ ".": "./index.ts",
26
+ "./*": "./*"
27
+ },
28
+ "scripts": {
29
+ "lint": "biome check",
30
+ "format": "prettier --write . && biome format --write",
31
+ "test": "vitest run",
32
+ "test:coverage": "vitest run --coverage"
33
+ },
34
+ "peerDependencies": {
35
+ "react": "^19.0.0",
36
+ "react-dom": "^19.0.0"
37
+ },
38
+ "devDependencies": {
39
+ "@biomejs/biome": "^2.5.4",
40
+ "@testing-library/jest-dom": "^6.6.3",
41
+ "@testing-library/react": "^16.2.0",
42
+ "@testing-library/user-event": "^14.6.1",
43
+ "@vitest/coverage-v8": "^4.1.10",
44
+ "jsdom": "^26.0.0",
45
+ "prettier": "^3.9.5",
46
+ "vitest": "^4.1.10"
47
+ }
15
48
  }
@@ -3,7 +3,7 @@
3
3
  import { useEffect } from "react";
4
4
 
5
5
  export function ScrolltoTop() {
6
- useEffect(() => {
7
- window.scrollTo(0, 0);
8
- });
6
+ useEffect(() => {
7
+ window.scrollTo(0, 0);
8
+ }, []);
9
9
  }
@@ -1,15 +1,15 @@
1
1
  import type { ReactNode } from "react";
2
2
 
3
3
  type SwitchCaseProps<T extends string | number> = {
4
- value: T;
5
- cases: Partial<Record<T, ReactNode>>;
6
- otherwise?: ReactNode;
4
+ value: T;
5
+ cases: Partial<Record<T, ReactNode>>;
6
+ otherwise?: ReactNode;
7
7
  };
8
8
 
9
9
  export function SwitchCase<T extends string | number>({
10
- value,
11
- cases,
12
- otherwise = null,
10
+ value,
11
+ cases,
12
+ otherwise = null,
13
13
  }: SwitchCaseProps<T>) {
14
- return (cases[value] ?? otherwise) as ReactNode;
14
+ return (cases[value] ?? otherwise) as ReactNode;
15
15
  }
@@ -6,35 +6,34 @@ import { createPortal } from "react-dom";
6
6
  const VIEWPORT_PORTAL_ROOT_ID = "viewport-portal-root";
7
7
 
8
8
  export function getViewportPortalRoot() {
9
- if (typeof document === "undefined") {
10
- return null;
11
- }
12
-
13
- const existingRoot = document.getElementById(VIEWPORT_PORTAL_ROOT_ID);
14
- if (existingRoot instanceof HTMLDivElement) {
15
- return existingRoot;
16
- }
17
-
18
- const root = document.createElement("div");
19
- root.id = VIEWPORT_PORTAL_ROOT_ID;
20
- root.setAttribute("data-viewport-portal-root", "true");
21
- root.style.cssText =
22
- "position:fixed;inset:0;pointer-events:none;z-index:9999;";
23
- document.body.appendChild(root);
24
- return root;
9
+ if (typeof document === "undefined") {
10
+ return null;
11
+ }
12
+
13
+ const existingRoot = document.getElementById(VIEWPORT_PORTAL_ROOT_ID);
14
+ if (existingRoot instanceof HTMLDivElement) {
15
+ return existingRoot;
16
+ }
17
+
18
+ const root = document.createElement("div");
19
+ root.id = VIEWPORT_PORTAL_ROOT_ID;
20
+ root.setAttribute("data-viewport-portal-root", "true");
21
+ root.style.cssText = "position:fixed;inset:0;pointer-events:none;z-index:9999;";
22
+ document.body.appendChild(root);
23
+ return root;
25
24
  }
26
25
 
27
26
  type ViewportPortalProps = {
28
- children: React.ReactNode;
27
+ children: React.ReactNode;
29
28
  };
30
29
 
31
30
  export function ViewportPortal({ children }: ViewportPortalProps) {
32
- const [root, setRoot] = useState<HTMLElement | null>(null);
31
+ const [root, setRoot] = useState<HTMLElement | null>(null);
33
32
 
34
- useEffect(() => {
35
- setRoot(getViewportPortalRoot());
36
- }, []);
33
+ useEffect(() => {
34
+ setRoot(getViewportPortalRoot());
35
+ }, []);
37
36
 
38
- if (!root) return null;
39
- return createPortal(children, root);
37
+ if (!root) return null;
38
+ return createPortal(children, root);
40
39
  }
@@ -3,140 +3,110 @@
3
3
  const EXPIRED_COOKIE_DATE = "Thu, 01 Jan 1970 00:00:00 GMT";
4
4
 
5
5
  type CookieStoreLike = {
6
- delete: (name: string) => Promise<void>;
6
+ delete: (name: string) => Promise<void>;
7
7
  };
8
8
 
9
9
  type DocumentCookieRef = {
10
- cookie: string;
10
+ cookie: string;
11
11
  };
12
12
 
13
13
  type ClearClientCookieOptions = {
14
- hostname?: string;
15
- path?: string;
16
- documentRef?: DocumentCookieRef;
17
- cookieStore?: CookieStoreLike;
14
+ hostname?: string;
15
+ path?: string;
16
+ documentRef?: DocumentCookieRef;
17
+ cookieStore?: CookieStoreLike;
18
18
  };
19
19
 
20
20
  type ClearAllClientCookiesOptions = ClearClientCookieOptions & {
21
- /**
22
- * If true, cookies in the hostname's root path ("/") are also deleted.
23
- * Use with caution.
24
- */
25
- includeRoot?: boolean;
26
- cookieString?: string;
21
+ includeRoot?: boolean;
22
+ cookieString?: string;
27
23
  };
28
24
 
29
- function findClientCookie(
30
- name: string,
31
- documentRef: DocumentCookieRef,
32
- ): string | undefined {
33
- const parsed = parseClientCookie(documentRef.cookie);
34
- return parsed[name];
25
+ function findClientCookie(name: string, documentRef: DocumentCookieRef): string | undefined {
26
+ const parsed = parseClientCookie(documentRef.cookie);
27
+ return parsed[name];
35
28
  }
36
29
 
37
30
  function parseClientCookie(cookieString: string): Record<string, string> {
38
- return cookieString
39
- .split(";")
40
- .map((pair) => pair.trim().split("=") as [string, string])
41
- .reduce<Record<string, string>>((acc, [key, value]) => {
42
- if (key) {
43
- acc[key] = value;
44
- }
45
- return acc;
46
- }, {});
31
+ return cookieString
32
+ .split(";")
33
+ .map((pair) => pair.trim().split("=") as [string, string])
34
+ .reduce<Record<string, string>>((acc, [key, value]) => {
35
+ if (key) {
36
+ acc[key] = value;
37
+ }
38
+ return acc;
39
+ }, {});
47
40
  }
48
41
 
49
- export function parseClientCookieNames(cookieString: string): string[] {
50
- return cookieString
51
- .split(";")
52
- .map((entry) => {
53
- const eqPos = entry.indexOf("=");
54
- return eqPos > -1 ? entry.slice(0, eqPos).trim() : entry.trim();
55
- })
56
- .filter(Boolean);
57
- }
42
+ import { parseClientCookieNames } from "./cookieNames.shared";
43
+ export { parseClientCookieNames };
58
44
 
59
45
  export function getClientCookie(name: string): string | undefined {
60
- if (typeof document === "undefined") {
61
- return undefined;
62
- }
46
+ if (typeof document === "undefined") {
47
+ return undefined;
48
+ }
63
49
 
64
- return findClientCookie(name, document);
50
+ return findClientCookie(name, document);
65
51
  }
66
52
 
67
53
  export function setClientCookie(name: string, value: string, days?: number) {
68
- if (typeof document === "undefined") {
69
- return;
70
- }
71
-
72
- let expires = "";
73
- if (days) {
74
- const date = new Date();
75
- date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
76
- expires = `; expires=${date.toUTCString()}`;
77
- }
78
-
79
- document.cookie = `${name}=${encodeURIComponent(value)}${expires}; path=/`;
54
+ if (typeof document === "undefined") {
55
+ return;
56
+ }
57
+
58
+ let expires = "";
59
+ if (days) {
60
+ const date = new Date();
61
+ date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
62
+ expires = `; expires=${date.toUTCString()}`;
63
+ }
64
+
65
+ document.cookie = `${name}=${encodeURIComponent(value)}${expires}; path=/`;
80
66
  }
81
67
 
82
- export function clearClientCookie(
83
- name: string,
84
- options: ClearClientCookieOptions = {},
85
- ): void {
86
- const {
87
- hostname = window.location.hostname,
88
- path = "/",
89
- documentRef = document,
90
- } = options;
91
-
92
- documentRef.cookie = `${name}=; expires=${EXPIRED_COOKIE_DATE}; path=${path}; domain=${hostname};`;
68
+ export function clearClientCookie(name: string, options: ClearClientCookieOptions = {}): void {
69
+ const { hostname = window.location.hostname, path = "/", documentRef = document } = options;
70
+
71
+ documentRef.cookie = `${name}=; expires=${EXPIRED_COOKIE_DATE}; path=${path}; domain=${hostname};`;
93
72
  }
94
73
 
95
- export function clearAllClientCookies(
96
- options: ClearAllClientCookiesOptions = {},
97
- ): string[] {
98
- const {
99
- documentRef = document,
100
- cookieStore,
101
- includeRoot = false,
102
- cookieString,
103
- } = options;
104
-
105
- const entries = cookieString
106
- ? parseClientCookieNames(cookieString)
107
- : documentRef.cookie
108
- .split(/;\s*/)
109
- .map((entry) => {
110
- const eqPos = entry.indexOf("=");
111
- return eqPos > -1 ? entry.slice(0, eqPos).trim() : entry.trim();
112
- })
113
- .filter(Boolean);
114
-
115
- if (cookieStore) {
116
- for (const name of entries) {
117
- cookieStore.delete(name);
118
- }
119
- return entries;
120
- }
121
-
122
- const hostnameParts = window.location.hostname.split(".");
123
- const { pathname } = window.location;
124
-
125
- const paths = [pathname];
126
- if (includeRoot) {
127
- paths.unshift("/");
128
- }
129
-
130
- for (const name of entries) {
131
- if (!name) continue;
132
-
133
- for (const path of paths) {
134
- for (let i = 0; i < hostnameParts.length; i++) {
135
- const domain = hostnameParts.slice(i).join(".");
136
- documentRef.cookie = `${name}=; expires=${EXPIRED_COOKIE_DATE}; path=${path}; domain=${domain};`;
137
- }
138
- }
139
- }
140
-
141
- return entries;
74
+ export function clearAllClientCookies(options: ClearAllClientCookiesOptions = {}): string[] {
75
+ const { documentRef = document, cookieStore, includeRoot = false, cookieString } = options;
76
+
77
+ const entries = cookieString
78
+ ? parseClientCookieNames(cookieString)
79
+ : documentRef.cookie
80
+ .split(/;\s*/)
81
+ .map((entry) => {
82
+ const eqPos = entry.indexOf("=");
83
+ return eqPos > -1 ? entry.slice(0, eqPos).trim() : entry.trim();
84
+ })
85
+ .filter(Boolean);
86
+
87
+ if (cookieStore) {
88
+ for (const name of entries) {
89
+ cookieStore.delete(name);
90
+ }
91
+ return entries;
92
+ }
93
+
94
+ const hostnameParts = window.location.hostname.split(".");
95
+ const { pathname } = window.location;
96
+
97
+ const paths = [pathname];
98
+ if (includeRoot) {
99
+ paths.unshift("/");
100
+ }
101
+
102
+ for (const name of entries) {
103
+ for (const path of paths) {
104
+ for (let i = 0; i < hostnameParts.length; i++) {
105
+ const domain = hostnameParts.slice(i).join(".");
106
+ documentRef.cookie = `${name}=; expires=${EXPIRED_COOKIE_DATE}; path=${path}; domain=${domain};`;
107
+ }
108
+ }
109
+ }
110
+
111
+ return entries;
142
112
  }
@@ -0,0 +1,9 @@
1
+ export function parseClientCookieNames(cookieString: string): string[] {
2
+ return cookieString
3
+ .split(";")
4
+ .map((entry) => {
5
+ const eqPos = entry.indexOf("=");
6
+ return eqPos > -1 ? entry.slice(0, eqPos).trim() : entry.trim();
7
+ })
8
+ .filter(Boolean);
9
+ }