@simplysm/core-browser 13.0.0-beta.1

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 (98) hide show
  1. package/.cache/typecheck-browser.tsbuildinfo +1 -0
  2. package/.cache/typecheck-tests-browser.tsbuildinfo +1 -0
  3. package/README.md +221 -0
  4. package/dist/core-browser/src/extensions/element-ext.d.ts +98 -0
  5. package/dist/core-browser/src/extensions/element-ext.d.ts.map +1 -0
  6. package/dist/core-browser/src/extensions/html-element-ext.d.ts +54 -0
  7. package/dist/core-browser/src/extensions/html-element-ext.d.ts.map +1 -0
  8. package/dist/core-browser/src/index.d.ts +7 -0
  9. package/dist/core-browser/src/index.d.ts.map +1 -0
  10. package/dist/core-browser/src/utils/blob.d.ts +10 -0
  11. package/dist/core-browser/src/utils/blob.d.ts.map +1 -0
  12. package/dist/core-browser/src/utils/download.d.ts +11 -0
  13. package/dist/core-browser/src/utils/download.d.ts.map +1 -0
  14. package/dist/core-common/src/common.types.d.ts +74 -0
  15. package/dist/core-common/src/common.types.d.ts.map +1 -0
  16. package/dist/core-common/src/env.d.ts +6 -0
  17. package/dist/core-common/src/env.d.ts.map +1 -0
  18. package/dist/core-common/src/errors/argument-error.d.ts +25 -0
  19. package/dist/core-common/src/errors/argument-error.d.ts.map +1 -0
  20. package/dist/core-common/src/errors/not-implemented-error.d.ts +29 -0
  21. package/dist/core-common/src/errors/not-implemented-error.d.ts.map +1 -0
  22. package/dist/core-common/src/errors/sd-error.d.ts +27 -0
  23. package/dist/core-common/src/errors/sd-error.d.ts.map +1 -0
  24. package/dist/core-common/src/errors/timeout-error.d.ts +31 -0
  25. package/dist/core-common/src/errors/timeout-error.d.ts.map +1 -0
  26. package/dist/core-common/src/extensions/arr-ext.d.ts +15 -0
  27. package/dist/core-common/src/extensions/arr-ext.d.ts.map +1 -0
  28. package/dist/core-common/src/extensions/arr-ext.helpers.d.ts +19 -0
  29. package/dist/core-common/src/extensions/arr-ext.helpers.d.ts.map +1 -0
  30. package/dist/core-common/src/extensions/arr-ext.types.d.ts +215 -0
  31. package/dist/core-common/src/extensions/arr-ext.types.d.ts.map +1 -0
  32. package/dist/core-common/src/extensions/map-ext.d.ts +57 -0
  33. package/dist/core-common/src/extensions/map-ext.d.ts.map +1 -0
  34. package/dist/core-common/src/extensions/set-ext.d.ts +36 -0
  35. package/dist/core-common/src/extensions/set-ext.d.ts.map +1 -0
  36. package/dist/core-common/src/features/debounce-queue.d.ts +53 -0
  37. package/dist/core-common/src/features/debounce-queue.d.ts.map +1 -0
  38. package/dist/core-common/src/features/event-emitter.d.ts +66 -0
  39. package/dist/core-common/src/features/event-emitter.d.ts.map +1 -0
  40. package/dist/core-common/src/features/serial-queue.d.ts +47 -0
  41. package/dist/core-common/src/features/serial-queue.d.ts.map +1 -0
  42. package/dist/core-common/src/index.d.ts +32 -0
  43. package/dist/core-common/src/index.d.ts.map +1 -0
  44. package/dist/core-common/src/types/date-only.d.ts +152 -0
  45. package/dist/core-common/src/types/date-only.d.ts.map +1 -0
  46. package/dist/core-common/src/types/date-time.d.ts +96 -0
  47. package/dist/core-common/src/types/date-time.d.ts.map +1 -0
  48. package/dist/core-common/src/types/lazy-gc-map.d.ts +80 -0
  49. package/dist/core-common/src/types/lazy-gc-map.d.ts.map +1 -0
  50. package/dist/core-common/src/types/time.d.ts +68 -0
  51. package/dist/core-common/src/types/time.d.ts.map +1 -0
  52. package/dist/core-common/src/types/uuid.d.ts +35 -0
  53. package/dist/core-common/src/types/uuid.d.ts.map +1 -0
  54. package/dist/core-common/src/utils/bytes.d.ts +51 -0
  55. package/dist/core-common/src/utils/bytes.d.ts.map +1 -0
  56. package/dist/core-common/src/utils/date-format.d.ts +90 -0
  57. package/dist/core-common/src/utils/date-format.d.ts.map +1 -0
  58. package/dist/core-common/src/utils/json.d.ts +34 -0
  59. package/dist/core-common/src/utils/json.d.ts.map +1 -0
  60. package/dist/core-common/src/utils/num.d.ts +60 -0
  61. package/dist/core-common/src/utils/num.d.ts.map +1 -0
  62. package/dist/core-common/src/utils/obj.d.ts +258 -0
  63. package/dist/core-common/src/utils/obj.d.ts.map +1 -0
  64. package/dist/core-common/src/utils/path.d.ts +23 -0
  65. package/dist/core-common/src/utils/path.d.ts.map +1 -0
  66. package/dist/core-common/src/utils/primitive.d.ts +18 -0
  67. package/dist/core-common/src/utils/primitive.d.ts.map +1 -0
  68. package/dist/core-common/src/utils/str.d.ts +103 -0
  69. package/dist/core-common/src/utils/str.d.ts.map +1 -0
  70. package/dist/core-common/src/utils/template-strings.d.ts +84 -0
  71. package/dist/core-common/src/utils/template-strings.d.ts.map +1 -0
  72. package/dist/core-common/src/utils/transferable.d.ts +47 -0
  73. package/dist/core-common/src/utils/transferable.d.ts.map +1 -0
  74. package/dist/core-common/src/utils/wait.d.ts +19 -0
  75. package/dist/core-common/src/utils/wait.d.ts.map +1 -0
  76. package/dist/core-common/src/utils/xml.d.ts +36 -0
  77. package/dist/core-common/src/utils/xml.d.ts.map +1 -0
  78. package/dist/core-common/src/zip/sd-zip.d.ts +80 -0
  79. package/dist/core-common/src/zip/sd-zip.d.ts.map +1 -0
  80. package/dist/extensions/element-ext.js +122 -0
  81. package/dist/extensions/element-ext.js.map +7 -0
  82. package/dist/extensions/html-element-ext.js +50 -0
  83. package/dist/extensions/html-element-ext.js.map +7 -0
  84. package/dist/index.js +7 -0
  85. package/dist/index.js.map +7 -0
  86. package/dist/utils/blob.js +19 -0
  87. package/dist/utils/blob.js.map +7 -0
  88. package/dist/utils/download.js +47 -0
  89. package/dist/utils/download.js.map +7 -0
  90. package/package.json +26 -0
  91. package/src/extensions/element-ext.ts +246 -0
  92. package/src/extensions/html-element-ext.ts +117 -0
  93. package/src/index.ts +11 -0
  94. package/src/utils/blob.ts +19 -0
  95. package/src/utils/download.ts +66 -0
  96. package/tests/extensions/element-ext.spec.ts +729 -0
  97. package/tests/extensions/html-element-ext.spec.ts +190 -0
  98. package/tests/utils/blob.spec.ts +68 -0
@@ -0,0 +1,190 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { ArgumentError } from "@simplysm/core-common";
3
+ import "../../src/extensions/html-element-ext";
4
+
5
+ describe("HTMLElement prototype extensions", () => {
6
+ let container: HTMLDivElement;
7
+
8
+ beforeEach(() => {
9
+ container = document.createElement("div");
10
+ document.body.appendChild(container);
11
+ });
12
+
13
+ afterEach(() => {
14
+ container.remove();
15
+ });
16
+
17
+ describe("repaint", () => {
18
+ it("offsetHeight 접근으로 리플로우 트리거", () => {
19
+ const el = document.createElement("div");
20
+ const offsetHeightSpy = vi.spyOn(el, "offsetHeight", "get").mockReturnValue(100);
21
+
22
+ el.repaint();
23
+
24
+ expect(offsetHeightSpy).toHaveBeenCalled();
25
+ });
26
+ });
27
+
28
+ describe("getRelativeOffset", () => {
29
+ it("부모 요소 기준 상대 위치 계산", () => {
30
+ container.style.position = "relative";
31
+ container.innerHTML = `<div id="child" style="position: absolute; top: 50px; left: 30px;"></div>`;
32
+
33
+ const child = container.querySelector<HTMLElement>("#child")!;
34
+
35
+ const result = child.getRelativeOffset(container);
36
+ expect(result).toHaveProperty("top");
37
+ expect(result).toHaveProperty("left");
38
+ expect(typeof result.top).toBe("number");
39
+ expect(typeof result.left).toBe("number");
40
+ });
41
+
42
+ it("셀렉터로 부모 찾기", () => {
43
+ container.id = "parent";
44
+ container.innerHTML = `<div><span id="deep-child"></span></div>`;
45
+
46
+ const deepChild = container.querySelector<HTMLElement>("#deep-child")!;
47
+
48
+ const result = deepChild.getRelativeOffset("#parent");
49
+ expect(result).toHaveProperty("top");
50
+ expect(result).toHaveProperty("left");
51
+ expect(typeof result.top).toBe("number");
52
+ expect(typeof result.left).toBe("number");
53
+ });
54
+
55
+ it("부모를 찾지 못하면 에러", () => {
56
+ const child = document.createElement("div");
57
+ container.appendChild(child);
58
+
59
+ expect(() => child.getRelativeOffset(".not-exist")).toThrow(ArgumentError);
60
+ });
61
+
62
+ it("transform이 적용된 요소도 처리", () => {
63
+ container.style.position = "relative";
64
+ container.style.transform = "scale(2)";
65
+ container.innerHTML = `<div id="child" style="transform: rotate(45deg);"></div>`;
66
+
67
+ const child = container.querySelector<HTMLElement>("#child")!;
68
+
69
+ // transform이 있어도 에러 없이 결과 반환해야 함
70
+ const result = child.getRelativeOffset(container);
71
+ expect(result).toHaveProperty("top");
72
+ expect(result).toHaveProperty("left");
73
+ expect(typeof result.top).toBe("number");
74
+ expect(typeof result.left).toBe("number");
75
+ });
76
+
77
+ it("중간 요소의 border 두께가 누적됨", () => {
78
+ container.style.position = "relative";
79
+ container.innerHTML = `
80
+ <div id="middle" style="border: 10px solid black;">
81
+ <div id="child"></div>
82
+ </div>
83
+ `;
84
+
85
+ const child = container.querySelector<HTMLElement>("#child")!;
86
+ const result = child.getRelativeOffset(container);
87
+
88
+ // 중간 요소의 borderTopWidth(10px)와 borderLeftWidth(10px)가 결과에 반영되어야 함
89
+ expect(result.top).toBeGreaterThanOrEqual(10);
90
+ expect(result.left).toBeGreaterThanOrEqual(10);
91
+ });
92
+
93
+ it("부모 요소가 스크롤된 상태에서 올바른 위치 계산", () => {
94
+ container.style.position = "relative";
95
+ container.style.overflow = "auto";
96
+ container.style.height = "100px";
97
+ container.style.width = "100px";
98
+
99
+ const inner = document.createElement("div");
100
+ inner.style.height = "500px";
101
+ inner.style.width = "500px";
102
+ inner.innerHTML = `<div id="child" style="position: absolute; top: 200px; left: 150px;"></div>`;
103
+ container.appendChild(inner);
104
+
105
+ // 부모 요소 스크롤
106
+ container.scrollTop = 50;
107
+ container.scrollLeft = 30;
108
+
109
+ const child = container.querySelector<HTMLElement>("#child")!;
110
+ const result = child.getRelativeOffset(container);
111
+
112
+ // scrollTop/scrollLeft가 결과에 반영됨 (parentEl.scrollTop + parentEl.scrollLeft 추가됨)
113
+ // 테스트 환경에서는 getBoundingClientRect가 스크롤을 반영하지 않으므로
114
+ // 최소한 scrollTop/scrollLeft 값이 추가되었는지 검증
115
+ expect(result.top).toBeGreaterThanOrEqual(200);
116
+ expect(result.left).toBeGreaterThanOrEqual(150);
117
+ });
118
+ });
119
+
120
+ describe("scrollIntoViewIfNeeded", () => {
121
+ it("대상이 offset보다 위에 있으면 스크롤", () => {
122
+ container.style.overflow = "auto";
123
+ container.style.height = "100px";
124
+ // 스크롤 가능한 콘텐츠 추가
125
+ const inner = document.createElement("div");
126
+ inner.style.height = "500px";
127
+ container.appendChild(inner);
128
+ container.scrollTop = 100;
129
+
130
+ container.scrollIntoViewIfNeeded({ top: 50, left: 0 }, { top: 10, left: 0 });
131
+
132
+ expect(container.scrollTop).toBe(40);
133
+ });
134
+
135
+ it("대상이 충분히 보이면 스크롤 안함", () => {
136
+ container.style.overflow = "auto";
137
+ container.style.height = "100px";
138
+ const inner = document.createElement("div");
139
+ inner.style.height = "500px";
140
+ container.appendChild(inner);
141
+ container.scrollTop = 0;
142
+
143
+ container.scrollIntoViewIfNeeded({ top: 50, left: 0 }, { top: 10, left: 0 });
144
+
145
+ expect(container.scrollTop).toBe(0);
146
+ });
147
+
148
+ it("기본 offset은 0", () => {
149
+ container.style.overflow = "auto";
150
+ container.style.height = "100px";
151
+ const inner = document.createElement("div");
152
+ inner.style.height = "500px";
153
+ container.appendChild(inner);
154
+ container.scrollTop = 100;
155
+
156
+ container.scrollIntoViewIfNeeded({ top: 50, left: 0 });
157
+
158
+ expect(container.scrollTop).toBe(50);
159
+ });
160
+
161
+ it("대상이 offset보다 왼쪽에 있으면 가로 스크롤", () => {
162
+ container.style.overflow = "auto";
163
+ container.style.width = "100px";
164
+ // 스크롤 가능한 콘텐츠 추가
165
+ const inner = document.createElement("div");
166
+ inner.style.width = "500px";
167
+ inner.style.height = "10px";
168
+ container.appendChild(inner);
169
+ container.scrollLeft = 100;
170
+
171
+ container.scrollIntoViewIfNeeded({ top: 0, left: 50 }, { top: 0, left: 10 });
172
+
173
+ expect(container.scrollLeft).toBe(40);
174
+ });
175
+
176
+ it("대상이 충분히 보이면 가로 스크롤 안함", () => {
177
+ container.style.overflow = "auto";
178
+ container.style.width = "100px";
179
+ const inner = document.createElement("div");
180
+ inner.style.width = "500px";
181
+ inner.style.height = "10px";
182
+ container.appendChild(inner);
183
+ container.scrollLeft = 0;
184
+
185
+ container.scrollIntoViewIfNeeded({ top: 0, left: 50 }, { top: 0, left: 10 });
186
+
187
+ expect(container.scrollLeft).toBe(0);
188
+ });
189
+ });
190
+ });
@@ -0,0 +1,68 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { BlobUtils } from "../../src/utils/blob";
3
+
4
+ describe("BlobUtils", () => {
5
+ let originalCreateObjectURL: typeof URL.createObjectURL;
6
+ let originalRevokeObjectURL: typeof URL.revokeObjectURL;
7
+ let mockLink: HTMLAnchorElement;
8
+ let clickSpy: ReturnType<typeof vi.fn>;
9
+
10
+ beforeEach(() => {
11
+ vi.useFakeTimers();
12
+
13
+ originalCreateObjectURL = URL.createObjectURL;
14
+ originalRevokeObjectURL = URL.revokeObjectURL;
15
+ URL.createObjectURL = vi.fn().mockReturnValue("blob:mock-url");
16
+ URL.revokeObjectURL = vi.fn();
17
+
18
+ clickSpy = vi.fn();
19
+ mockLink = {
20
+ href: "",
21
+ download: "",
22
+ click: clickSpy,
23
+ } as unknown as HTMLAnchorElement;
24
+
25
+ vi.spyOn(document, "createElement").mockReturnValue(mockLink);
26
+ });
27
+
28
+ afterEach(() => {
29
+ vi.useRealTimers();
30
+ URL.createObjectURL = originalCreateObjectURL;
31
+ URL.revokeObjectURL = originalRevokeObjectURL;
32
+ vi.restoreAllMocks();
33
+ });
34
+
35
+ describe("download", () => {
36
+ it("Blob을 다운로드 링크로 변환하여 클릭", () => {
37
+ const blob = new Blob(["test content"], { type: "text/plain" });
38
+ const fileName = "test.txt";
39
+
40
+ BlobUtils.download(blob, fileName);
41
+
42
+ expect(URL.createObjectURL).toHaveBeenCalledWith(blob);
43
+ expect(mockLink.href).toBe("blob:mock-url");
44
+ expect(mockLink.download).toBe(fileName);
45
+ expect(clickSpy).toHaveBeenCalled();
46
+ });
47
+
48
+ it("다운로드 후 URL.revokeObjectURL 호출 (메모리 누수 방지)", () => {
49
+ const blob = new Blob(["test"], { type: "text/plain" });
50
+
51
+ BlobUtils.download(blob, "test.txt");
52
+ vi.runAllTimers();
53
+
54
+ expect(URL.revokeObjectURL).toHaveBeenCalledWith("blob:mock-url");
55
+ });
56
+
57
+ it("에러 발생 시에도 revokeObjectURL 호출", () => {
58
+ const blob = new Blob(["test"], { type: "text/plain" });
59
+ clickSpy.mockImplementation(() => {
60
+ throw new Error("Click failed");
61
+ });
62
+
63
+ expect(() => BlobUtils.download(blob, "test.txt")).toThrow("Click failed");
64
+ vi.runAllTimers();
65
+ expect(URL.revokeObjectURL).toHaveBeenCalledWith("blob:mock-url");
66
+ });
67
+ });
68
+ });