@simplysm/core-browser 13.0.84 → 13.0.86
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 +298 -16
- package/dist/extensions/element-ext.js +2 -2
- package/dist/extensions/element-ext.js.map +1 -1
- package/dist/utils/IndexedDbStore.d.ts +5 -0
- package/dist/utils/IndexedDbStore.d.ts.map +1 -1
- package/dist/utils/IndexedDbStore.js +77 -49
- package/dist/utils/IndexedDbStore.js.map +1 -1
- package/dist/utils/IndexedDbVirtualFs.js +2 -2
- package/dist/utils/IndexedDbVirtualFs.js.map +1 -1
- package/package.json +3 -3
- package/src/extensions/element-ext.ts +2 -2
- package/src/utils/IndexedDbStore.ts +87 -53
- package/src/utils/IndexedDbVirtualFs.ts +2 -2
- package/tests/extensions/element-ext.spec.ts +34 -0
- package/tests/utils/IndexedDbStore.spec.ts +103 -0
- package/tests/utils/IndexedDbVirtualFs.spec.ts +171 -0
- package/tests/utils/fetch.spec.ts +154 -0
- package/docs/element-extensions.md +0 -151
- package/docs/html-element-extensions.md +0 -86
- package/docs/indexed-db.md +0 -193
- package/docs/utilities.md +0 -94
package/README.md
CHANGED
|
@@ -1,29 +1,311 @@
|
|
|
1
1
|
# @simplysm/core-browser
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
브라우저 환경 전용 유틸리티. DOM 확장, 파일 다운로드, IndexedDB 추상화를 제공한다.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## Installation
|
|
5
|
+
## 설치
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
8
|
npm install @simplysm/core-browser
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
**의존성:** `@simplysm/core-common`, `tabbable`
|
|
12
|
+
|
|
13
|
+
## 주요 기능
|
|
14
|
+
|
|
15
|
+
### Element 확장 메서드
|
|
16
|
+
|
|
17
|
+
`import "@simplysm/core-browser"` (side-effect import) 시 `Element.prototype`에 추가된다.
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import "@simplysm/core-browser";
|
|
21
|
+
|
|
22
|
+
// 요소 검색
|
|
23
|
+
el.findAll<HTMLDivElement>(".item"); // 자식 요소 전체 검색
|
|
24
|
+
el.findFirst<HTMLInputElement>("input"); // 첫 번째 매칭 요소
|
|
25
|
+
|
|
26
|
+
// DOM 조작
|
|
27
|
+
el.prependChild(newChild); // 첫 번째 자식으로 삽입
|
|
28
|
+
|
|
29
|
+
// 탐색
|
|
30
|
+
el.getParents(); // 모든 부모 요소 (가까운 순)
|
|
31
|
+
el.findFocusableParent(); // 포커스 가능한 부모 찾기
|
|
32
|
+
el.findFirstFocusableChild(); // 포커스 가능한 첫 자식 찾기
|
|
33
|
+
|
|
34
|
+
// 상태 확인
|
|
35
|
+
el.isOffsetElement(); // position: relative/absolute/fixed/sticky 여부
|
|
36
|
+
el.isVisible(); // 가시성 확인 (clientRects, visibility, opacity)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
#### getBounds -- 요소 크기/위치 일괄 조회
|
|
40
|
+
|
|
41
|
+
IntersectionObserver 기반으로 여러 요소의 뷰포트 기준 위치/크기를 비동기로 조회한다.
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { getBounds } from "@simplysm/core-browser";
|
|
45
|
+
import type { ElementBounds } from "@simplysm/core-browser";
|
|
46
|
+
|
|
47
|
+
const bounds: ElementBounds[] = await getBounds([el1, el2, el3], 5000);
|
|
48
|
+
// 타임아웃 기본 5000ms, 초과 시 TimeoutError 발생
|
|
49
|
+
|
|
50
|
+
bounds[0]; // { target: Element, top: number, left: number, width: number, height: number }
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**시그니처:**
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
interface ElementBounds {
|
|
57
|
+
target: Element;
|
|
58
|
+
top: number;
|
|
59
|
+
left: number;
|
|
60
|
+
width: number;
|
|
61
|
+
height: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function getBounds(els: Element[], timeout?: number): Promise<ElementBounds[]>;
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
- 중복 요소는 자동 제거되며, 결과는 입력 순서대로 정렬된다.
|
|
68
|
+
- 빈 배열 입력 시 빈 배열을 즉시 반환한다.
|
|
69
|
+
- 타임아웃 초과 시 `TimeoutError` (`@simplysm/core-common`)를 throw한다.
|
|
70
|
+
|
|
71
|
+
#### 클립보드 -- copyElement / pasteToElement
|
|
72
|
+
|
|
73
|
+
copy/paste 이벤트 핸들러에서 사용한다. 대상 요소 내부의 첫 번째 `input`/`textarea`를 자동으로 찾아 처리한다.
|
|
14
74
|
|
|
15
|
-
|
|
75
|
+
```typescript
|
|
76
|
+
import { copyElement, pasteToElement } from "@simplysm/core-browser";
|
|
16
77
|
|
|
17
|
-
|
|
78
|
+
document.addEventListener("copy", (event) => {
|
|
79
|
+
copyElement(event); // input/textarea의 value를 클립보드에 복사
|
|
80
|
+
});
|
|
18
81
|
|
|
19
|
-
|
|
20
|
-
|
|
82
|
+
document.addEventListener("paste", (event) => {
|
|
83
|
+
pasteToElement(event); // 클립보드 텍스트를 input/textarea에 붙여넣기
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**시그니처:**
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
function copyElement(event: ClipboardEvent): void;
|
|
91
|
+
function pasteToElement(event: ClipboardEvent): void;
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
- `pasteToElement`는 입력 요소의 전체 value를 교체하며, `input` 이벤트를 `bubbles: true`로 dispatch한다.
|
|
95
|
+
- 대상 요소가 `Element`가 아니거나 `clipboardData`가 없으면 무시한다.
|
|
96
|
+
|
|
97
|
+
### HTMLElement 확장 메서드
|
|
98
|
+
|
|
99
|
+
side-effect import 시 `HTMLElement.prototype`에 추가된다.
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
el.repaint(); // 강제 리페인트 (reflow 트리거)
|
|
103
|
+
|
|
104
|
+
// 상대 위치 계산 (transform 포함)
|
|
105
|
+
// parent: HTMLElement 또는 CSS 선택자 문자열
|
|
106
|
+
const offset = el.getRelativeOffset(parentEl);
|
|
107
|
+
const offset2 = el.getRelativeOffset(".container");
|
|
108
|
+
// { top: number, left: number } -- CSS top/left에 직접 사용 가능
|
|
109
|
+
// 뷰포트 좌표, 스크롤 위치, 보더, transform을 모두 반영
|
|
110
|
+
|
|
111
|
+
// 고정 영역을 고려한 스크롤
|
|
112
|
+
el.scrollIntoViewIfNeeded(
|
|
113
|
+
{ top: 100, left: 0 }, // 대상 위치 (offsetTop, offsetLeft)
|
|
114
|
+
{ top: 60, left: 0 }, // 오프셋 (고정 헤더 높이 등)
|
|
115
|
+
);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**시그니처:**
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
interface HTMLElement {
|
|
122
|
+
repaint(): void;
|
|
123
|
+
getRelativeOffset(parent: HTMLElement | string): { top: number; left: number };
|
|
124
|
+
scrollIntoViewIfNeeded(
|
|
125
|
+
target: { top: number; left: number },
|
|
126
|
+
offset?: { top: number; left: number },
|
|
127
|
+
): void;
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
- `getRelativeOffset`: 부모 요소를 찾을 수 없으면 `ArgumentError` (`@simplysm/core-common`)를 throw한다. 드롭다운/팝업을 `document.body`에 append한 뒤 위치를 잡을 때 유용하다.
|
|
132
|
+
- `scrollIntoViewIfNeeded`: 위쪽/왼쪽 방향 스크롤만 처리한다. 아래쪽/오른쪽은 브라우저 기본 포커스 스크롤에 위임한다.
|
|
133
|
+
|
|
134
|
+
### 파일 다운로드
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { downloadBlob } from "@simplysm/core-browser";
|
|
138
|
+
|
|
139
|
+
downloadBlob(blob, "report.xlsx");
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**시그니처:**
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
function downloadBlob(blob: Blob, fileName: string): void;
|
|
146
|
+
```
|
|
21
147
|
|
|
22
|
-
|
|
148
|
+
내부적으로 `URL.createObjectURL`을 생성하고 1초 후 해제한다.
|
|
149
|
+
|
|
150
|
+
### 바이너리 다운로드 (진행률 지원)
|
|
151
|
+
|
|
152
|
+
URL에서 바이너리 데이터를 다운로드한다. Content-Length가 존재하면 미리 할당하여 메모리 효율적으로 처리하고, 없으면 chunked encoding 방식으로 수집 후 병합한다.
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
import { fetchUrlBytes } from "@simplysm/core-browser";
|
|
156
|
+
import type { DownloadProgress } from "@simplysm/core-browser";
|
|
157
|
+
|
|
158
|
+
const data: Uint8Array = await fetchUrlBytes("/api/file", {
|
|
159
|
+
onProgress: ({ receivedLength, contentLength }: DownloadProgress) => {
|
|
160
|
+
// contentLength가 0이면 Content-Length 헤더가 없는 경우
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**시그니처:**
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
interface DownloadProgress {
|
|
169
|
+
receivedLength: number;
|
|
170
|
+
contentLength: number;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function fetchUrlBytes(
|
|
174
|
+
url: string,
|
|
175
|
+
options?: { onProgress?: (progress: DownloadProgress) => void },
|
|
176
|
+
): Promise<Uint8Array>;
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
- HTTP 에러 응답 시 `Error("Download failed: {status} {statusText}")`를 throw한다.
|
|
180
|
+
- response body를 읽을 수 없는 경우 `Error("Response body is not readable")`를 throw한다.
|
|
181
|
+
- Content-Length가 없는 경우 `onProgress` 콜백의 `contentLength`는 `0`이며, 콜백이 호출되지 않는다.
|
|
182
|
+
|
|
183
|
+
### 파일 선택 다이얼로그
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
import { openFileDialog } from "@simplysm/core-browser";
|
|
187
|
+
|
|
188
|
+
const files = await openFileDialog({
|
|
189
|
+
accept: ".json",
|
|
190
|
+
multiple: true,
|
|
191
|
+
});
|
|
192
|
+
// File[] | undefined (취소 시 undefined)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**시그니처:**
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
function openFileDialog(options?: {
|
|
199
|
+
accept?: string;
|
|
200
|
+
multiple?: boolean;
|
|
201
|
+
}): Promise<File[] | undefined>;
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### IndexedDB 스토어
|
|
205
|
+
|
|
206
|
+
IndexedDB를 간단한 key-value 스토어로 추상화한다. `open()` 중복 호출 시 기존 연결을 재사용하며, 버전 변경/연결 종료 시 자동으로 상태를 정리한다.
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
import { IndexedDbStore } from "@simplysm/core-browser";
|
|
210
|
+
import type { StoreConfig } from "@simplysm/core-browser";
|
|
211
|
+
|
|
212
|
+
const store = new IndexedDbStore("myApp", 1, [
|
|
213
|
+
{ name: "users", keyPath: "id" },
|
|
214
|
+
{ name: "settings", keyPath: "key" },
|
|
215
|
+
]);
|
|
216
|
+
|
|
217
|
+
await store.open();
|
|
218
|
+
|
|
219
|
+
// CRUD
|
|
220
|
+
await store.put("users", { id: "1", name: "Alice" });
|
|
221
|
+
const user = await store.get<User>("users", "1");
|
|
222
|
+
const all = await store.getAll<User>("users");
|
|
223
|
+
await store.delete("users", "1");
|
|
224
|
+
|
|
225
|
+
// 트랜잭션 스코프 (IDBObjectStore 직접 조작)
|
|
226
|
+
const result = await store.withStore("users", "readwrite", async (objStore) => {
|
|
227
|
+
// IDBObjectStore API 직접 사용
|
|
228
|
+
return someValue;
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
store.close();
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**시그니처:**
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
interface StoreConfig {
|
|
238
|
+
name: string;
|
|
239
|
+
keyPath: string;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
class IndexedDbStore {
|
|
243
|
+
constructor(dbName: string, dbVersion: number, storeConfigs: StoreConfig[]);
|
|
244
|
+
open(): Promise<IDBDatabase>;
|
|
245
|
+
get<TValue>(storeName: string, key: IDBValidKey): Promise<TValue | undefined>;
|
|
246
|
+
put(storeName: string, value: unknown): Promise<void>;
|
|
247
|
+
delete(storeName: string, key: IDBValidKey): Promise<void>;
|
|
248
|
+
getAll<TItem>(storeName: string): Promise<TItem[]>;
|
|
249
|
+
withStore<TResult>(
|
|
250
|
+
storeName: string,
|
|
251
|
+
mode: IDBTransactionMode,
|
|
252
|
+
fn: (store: IDBObjectStore) => Promise<TResult>,
|
|
253
|
+
): Promise<TResult>;
|
|
254
|
+
close(): void;
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
- `open()`은 동시 호출 시 하나의 Promise만 생성하여 재사용한다.
|
|
259
|
+
- 다른 연결에서 버전이 변경되면(`onversionchange`) 자동으로 연결을 닫고 상태를 초기화한다.
|
|
260
|
+
- 다른 연결에 의해 blocked 상태가 되면 `Error("Database blocked by another connection")`를 throw한다.
|
|
261
|
+
- `withStore`에서 `fn` 실행 중 에러 발생 시 트랜잭션을 abort한 뒤 원래 에러를 다시 throw한다.
|
|
262
|
+
|
|
263
|
+
### IndexedDB 가상 파일시스템
|
|
264
|
+
|
|
265
|
+
IndexedDB 위에 계층적 파일/디렉토리 구조를 구현한다. `IndexedDbStore`를 내부 저장소로 사용한다.
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
import { IndexedDbVirtualFs, IndexedDbStore } from "@simplysm/core-browser";
|
|
269
|
+
import type { VirtualFsEntry } from "@simplysm/core-browser";
|
|
270
|
+
|
|
271
|
+
const dbStore = new IndexedDbStore("vfs", 1, [{ name: "files", keyPath: "key" }]);
|
|
272
|
+
const vfs = new IndexedDbVirtualFs(dbStore, "files", "key");
|
|
273
|
+
|
|
274
|
+
// 디렉토리 생성 (중간 경로 자동 생성)
|
|
275
|
+
await vfs.ensureDir((p) => `root${p}`, "documents/reports");
|
|
276
|
+
|
|
277
|
+
// 파일 읽기/쓰기
|
|
278
|
+
await vfs.putEntry("root/hello.txt", "file", btoa("Hello"));
|
|
279
|
+
const entry = await vfs.getEntry("root/hello.txt");
|
|
280
|
+
// { kind: "file", dataBase64: "SGVsbG8=" } | undefined
|
|
281
|
+
|
|
282
|
+
// 목록 조회
|
|
283
|
+
const children = await vfs.listChildren("root/");
|
|
284
|
+
// [{ name: "documents", isDirectory: true }, { name: "hello.txt", isDirectory: false }]
|
|
285
|
+
|
|
286
|
+
// 삭제 (키 자체 + 하위 항목 모두 삭제)
|
|
287
|
+
const deleted: boolean = await vfs.deleteByPrefix("root/documents");
|
|
288
|
+
// true: 삭제된 항목이 있음, false: 해당 키 prefix에 항목 없음
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
**시그니처:**
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
interface VirtualFsEntry {
|
|
295
|
+
kind: "file" | "dir";
|
|
296
|
+
dataBase64?: string;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
class IndexedDbVirtualFs {
|
|
300
|
+
constructor(db: IndexedDbStore, storeName: string, keyField: string);
|
|
301
|
+
getEntry(fullKey: string): Promise<VirtualFsEntry | undefined>;
|
|
302
|
+
putEntry(fullKey: string, kind: "file" | "dir", dataBase64?: string): Promise<void>;
|
|
303
|
+
deleteByPrefix(keyPrefix: string): Promise<boolean>;
|
|
304
|
+
listChildren(prefix: string): Promise<{ name: string; isDirectory: boolean }[]>;
|
|
305
|
+
ensureDir(fullKeyBuilder: (path: string) => string, dirPath: string): Promise<void>;
|
|
306
|
+
}
|
|
307
|
+
```
|
|
23
308
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
| HTMLElement Extensions | `HTMLElement` prototype methods for layout and scrolling | [docs/html-element-extensions.md](docs/html-element-extensions.md) |
|
|
28
|
-
| Utilities | File download, fetch with progress, file dialog | [docs/utilities.md](docs/utilities.md) |
|
|
29
|
-
| IndexedDB | IndexedDB store wrapper and virtual filesystem | [docs/indexed-db.md](docs/indexed-db.md) |
|
|
309
|
+
- `deleteByPrefix`는 정확히 일치하는 키와 `keyPrefix + "/"` 로 시작하는 하위 항목을 모두 삭제한다.
|
|
310
|
+
- `listChildren`은 직접 자식만 반환한다 (중첩된 하위 항목은 디렉토리로 표시).
|
|
311
|
+
- `ensureDir`은 경로의 각 세그먼트에 대해 이미 존재하는지 확인 후 없으면 생성한다.
|
|
@@ -54,10 +54,10 @@ function copyElement(event) {
|
|
|
54
54
|
const clipboardData = event.clipboardData;
|
|
55
55
|
const target = event.target;
|
|
56
56
|
if (clipboardData == null || !(target instanceof Element)) return;
|
|
57
|
-
const firstInputEl = target.
|
|
57
|
+
const firstInputEl = target.findFirst(
|
|
58
58
|
"input, textarea"
|
|
59
59
|
);
|
|
60
|
-
if (firstInputEl
|
|
60
|
+
if (firstInputEl !== void 0) {
|
|
61
61
|
clipboardData.setData("text/plain", firstInputEl.value);
|
|
62
62
|
event.preventDefault();
|
|
63
63
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/extensions/element-ext.ts"],
|
|
4
|
-
"mappings": "AAAA,SAAS,mBAAmB;AAC5B,SAAS,oBAAoB;AAoF7B,QAAQ,UAAU,UAAU,SAAyC,UAAyB;AAC5F,QAAM,UAAU,SAAS,KAAK;AAC9B,MAAI,YAAY,GAAI,QAAO,CAAC;AAC5B,SAAO,MAAM,KAAK,KAAK,iBAAsB,OAAO,CAAC;AACvD;AAEA,QAAQ,UAAU,YAAY,SAC5B,UACiB;AACjB,QAAM,UAAU,SAAS,KAAK;AAC9B,MAAI,YAAY,GAAI,QAAO;AAC3B,SAAO,KAAK,cAAmB,OAAO,KAAK;AAC7C;AAEA,QAAQ,UAAU,eAAe,SAA+B,OAAiB;AAC/E,SAAO,KAAK,aAAa,OAAO,KAAK,iBAAiB;AACxD;AAEA,QAAQ,UAAU,aAAa,WAAuB;AACpD,QAAM,SAAoB,CAAC;AAC3B,MAAI,SAAS,KAAK;AAClB,SAAO,WAAW,QAAQ,kBAAkB,SAAS;AACnD,WAAO,KAAK,MAAM;AAClB,aAAS,OAAO;AAAA,EAClB;AACA,SAAO;AACT;AAEA,QAAQ,UAAU,sBAAsB,WAAqC;AAC3E,MAAI,WAAW,KAAK;AACpB,SAAO,aAAa,MAAM;AACxB,QAAI,YAAY,QAAQ,GAAG;AACzB,aAAO;AAAA,IACT;AACA,eAAW,SAAS;AAAA,EACtB;AACA,SAAO;AACT;AAEA,QAAQ,UAAU,0BAA0B,WAAqC;AAC/E,QAAM,SAAS,SAAS,iBAAiB,MAAM,WAAW,YAAY;AACtE,MAAI,OAAO,OAAO,SAAS;AAC3B,SAAO,SAAS,MAAM;AACpB,QAAI,gBAAgB,eAAe,YAAY,IAAI,GAAG;AACpD,aAAO;AAAA,IACT;AACA,WAAO,OAAO,SAAS;AAAA,EACzB;AACA,SAAO;AACT;AAEA,QAAQ,UAAU,kBAAkB,WAAqB;AACvD,SAAO,CAAC,YAAY,YAAY,SAAS,QAAQ,EAAE,SAAS,iBAAiB,IAAI,EAAE,QAAQ;AAC7F;AAEA,QAAQ,UAAU,YAAY,WAAqB;AACjD,QAAM,QAAQ,iBAAiB,IAAI;AACnC,SAAO,KAAK,eAAe,EAAE,SAAS,KAAK,MAAM,eAAe,YAAY,MAAM,YAAY;AAChG;AAWO,SAAS,YAAY,OAA6B;AACvD,QAAM,gBAAgB,MAAM;AAC5B,QAAM,SAAS,MAAM;AACrB,MAAI,iBAAiB,QAAQ,EAAE,kBAAkB,SAAU;AAE3D,QAAM,eAAe,OAAO;AAAA,IAC1B;AAAA,EACF;AACA,MAAI,
|
|
4
|
+
"mappings": "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",
|
|
5
5
|
"names": []
|
|
6
6
|
}
|
|
@@ -6,11 +6,16 @@ export declare class IndexedDbStore {
|
|
|
6
6
|
private readonly _dbName;
|
|
7
7
|
private readonly _dbVersion;
|
|
8
8
|
private readonly _storeConfigs;
|
|
9
|
+
private _db;
|
|
10
|
+
private _opening;
|
|
9
11
|
constructor(_dbName: string, _dbVersion: number, _storeConfigs: StoreConfig[]);
|
|
10
12
|
open(): Promise<IDBDatabase>;
|
|
11
13
|
withStore<TResult>(storeName: string, mode: IDBTransactionMode, fn: (store: IDBObjectStore) => Promise<TResult>): Promise<TResult>;
|
|
12
14
|
get<TValue>(storeName: string, key: IDBValidKey): Promise<TValue | undefined>;
|
|
13
15
|
put(storeName: string, value: unknown): Promise<void>;
|
|
16
|
+
delete(storeName: string, key: IDBValidKey): Promise<void>;
|
|
14
17
|
getAll<TItem>(storeName: string): Promise<TItem[]>;
|
|
18
|
+
private _resolveRequest;
|
|
19
|
+
close(): void;
|
|
15
20
|
}
|
|
16
21
|
//# sourceMappingURL=IndexedDbStore.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IndexedDbStore.d.ts","sourceRoot":"","sources":["..\\..\\src\\utils\\IndexedDbStore.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,cAAc;
|
|
1
|
+
{"version":3,"file":"IndexedDbStore.d.ts","sourceRoot":"","sources":["..\\..\\src\\utils\\IndexedDbStore.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,cAAc;IAKvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,aAAa;IANhC,OAAO,CAAC,GAAG,CAA0B;IACrC,OAAO,CAAC,QAAQ,CAAmC;gBAGhC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,WAAW,EAAE;IAGzC,IAAI,IAAI,OAAO,CAAC,WAAW,CAAC;IA+C5B,SAAS,CAAC,OAAO,EACrB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,kBAAkB,EACxB,EAAE,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,CAAC,GAC9C,OAAO,CAAC,OAAO,CAAC;IAkCb,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAM7E,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAMrD,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAM1D,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAMxD,OAAO,CAAC,eAAe;IAOvB,KAAK,IAAI,IAAI;CAKd"}
|
|
@@ -4,8 +4,16 @@ class IndexedDbStore {
|
|
|
4
4
|
this._dbVersion = _dbVersion;
|
|
5
5
|
this._storeConfigs = _storeConfigs;
|
|
6
6
|
}
|
|
7
|
+
_db;
|
|
8
|
+
_opening;
|
|
7
9
|
async open() {
|
|
8
|
-
|
|
10
|
+
if (this._db != null) {
|
|
11
|
+
return this._db;
|
|
12
|
+
}
|
|
13
|
+
if (this._opening != null) {
|
|
14
|
+
return this._opening;
|
|
15
|
+
}
|
|
16
|
+
this._opening = new Promise((resolve, reject) => {
|
|
9
17
|
const req = indexedDB.open(this._dbName, this._dbVersion);
|
|
10
18
|
req.onupgradeneeded = () => {
|
|
11
19
|
const db = req.result;
|
|
@@ -15,73 +23,93 @@ class IndexedDbStore {
|
|
|
15
23
|
}
|
|
16
24
|
}
|
|
17
25
|
};
|
|
18
|
-
req.onsuccess = () =>
|
|
19
|
-
|
|
20
|
-
|
|
26
|
+
req.onsuccess = () => {
|
|
27
|
+
const db = req.result;
|
|
28
|
+
db.onversionchange = () => {
|
|
29
|
+
db.close();
|
|
30
|
+
this._db = void 0;
|
|
31
|
+
this._opening = void 0;
|
|
32
|
+
};
|
|
33
|
+
db.onclose = () => {
|
|
34
|
+
this._db = void 0;
|
|
35
|
+
this._opening = void 0;
|
|
36
|
+
};
|
|
37
|
+
this._db = db;
|
|
38
|
+
this._opening = void 0;
|
|
39
|
+
resolve(db);
|
|
40
|
+
};
|
|
41
|
+
req.onerror = () => {
|
|
42
|
+
this._opening = void 0;
|
|
43
|
+
reject(req.error);
|
|
44
|
+
};
|
|
45
|
+
req.onblocked = () => {
|
|
46
|
+
this._opening = void 0;
|
|
47
|
+
reject(new Error("Database blocked by another connection"));
|
|
48
|
+
};
|
|
21
49
|
});
|
|
50
|
+
return this._opening;
|
|
22
51
|
}
|
|
23
52
|
async withStore(storeName, mode, fn) {
|
|
24
53
|
const db = await this.open();
|
|
54
|
+
const tx = db.transaction(storeName, mode);
|
|
55
|
+
const store = tx.objectStore(storeName);
|
|
56
|
+
let result;
|
|
57
|
+
let fnError;
|
|
58
|
+
let hasFnError = false;
|
|
25
59
|
try {
|
|
26
|
-
|
|
27
|
-
const store = tx.objectStore(storeName);
|
|
28
|
-
let result;
|
|
29
|
-
let fnError;
|
|
30
|
-
try {
|
|
31
|
-
result = await fn(store);
|
|
32
|
-
} catch (err) {
|
|
33
|
-
fnError = err;
|
|
34
|
-
tx.abort();
|
|
35
|
-
}
|
|
36
|
-
return await new Promise((resolve, reject) => {
|
|
37
|
-
if (fnError !== void 0) {
|
|
38
|
-
tx.onerror = () => {
|
|
39
|
-
db.close();
|
|
40
|
-
reject(fnError);
|
|
41
|
-
};
|
|
42
|
-
} else {
|
|
43
|
-
tx.oncomplete = () => {
|
|
44
|
-
db.close();
|
|
45
|
-
resolve(result);
|
|
46
|
-
};
|
|
47
|
-
tx.onerror = () => {
|
|
48
|
-
db.close();
|
|
49
|
-
reject(tx.error);
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
});
|
|
60
|
+
result = await fn(store);
|
|
53
61
|
} catch (err) {
|
|
54
|
-
|
|
55
|
-
|
|
62
|
+
fnError = err;
|
|
63
|
+
hasFnError = true;
|
|
64
|
+
tx.abort();
|
|
56
65
|
}
|
|
66
|
+
return new Promise((resolve, reject) => {
|
|
67
|
+
if (hasFnError) {
|
|
68
|
+
tx.onabort = () => {
|
|
69
|
+
reject(fnError);
|
|
70
|
+
};
|
|
71
|
+
} else {
|
|
72
|
+
tx.oncomplete = () => {
|
|
73
|
+
resolve(result);
|
|
74
|
+
};
|
|
75
|
+
tx.onerror = () => {
|
|
76
|
+
reject(tx.error);
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
});
|
|
57
80
|
}
|
|
58
81
|
async get(storeName, key) {
|
|
59
82
|
return this.withStore(storeName, "readonly", async (store) => {
|
|
60
|
-
return
|
|
61
|
-
const req = store.get(key);
|
|
62
|
-
req.onsuccess = () => resolve(req.result);
|
|
63
|
-
req.onerror = () => reject(req.error);
|
|
64
|
-
});
|
|
83
|
+
return this._resolveRequest(store.get(key));
|
|
65
84
|
});
|
|
66
85
|
}
|
|
67
86
|
async put(storeName, value) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
87
|
+
await this.withStore(storeName, "readwrite", async (store) => {
|
|
88
|
+
await this._resolveRequest(store.put(value));
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
async delete(storeName, key) {
|
|
92
|
+
await this.withStore(storeName, "readwrite", async (store) => {
|
|
93
|
+
await this._resolveRequest(store.delete(key));
|
|
74
94
|
});
|
|
75
95
|
}
|
|
76
96
|
async getAll(storeName) {
|
|
77
97
|
return this.withStore(storeName, "readonly", async (store) => {
|
|
78
|
-
return
|
|
79
|
-
const req = store.getAll();
|
|
80
|
-
req.onsuccess = () => resolve(req.result);
|
|
81
|
-
req.onerror = () => reject(req.error);
|
|
82
|
-
});
|
|
98
|
+
return this._resolveRequest(store.getAll());
|
|
83
99
|
});
|
|
84
100
|
}
|
|
101
|
+
_resolveRequest(req) {
|
|
102
|
+
return new Promise((resolve, reject) => {
|
|
103
|
+
req.onsuccess = () => resolve(req.result);
|
|
104
|
+
req.onerror = () => reject(req.error);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
close() {
|
|
108
|
+
var _a;
|
|
109
|
+
(_a = this._db) == null ? void 0 : _a.close();
|
|
110
|
+
this._db = void 0;
|
|
111
|
+
this._opening = void 0;
|
|
112
|
+
}
|
|
85
113
|
}
|
|
86
114
|
export {
|
|
87
115
|
IndexedDbStore
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/utils/IndexedDbStore.ts"],
|
|
4
|
-
"mappings": "AAKO,MAAM,eAAe;AAAA,
|
|
4
|
+
"mappings": "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",
|
|
5
5
|
"names": []
|
|
6
6
|
}
|
|
@@ -13,7 +13,7 @@ class IndexedDbVirtualFs {
|
|
|
13
13
|
async deleteByPrefix(keyPrefix) {
|
|
14
14
|
return this._db.withStore(this._storeName, "readwrite", async (store) => {
|
|
15
15
|
return new Promise((resolve, reject) => {
|
|
16
|
-
const req = store.openCursor();
|
|
16
|
+
const req = store.openCursor(IDBKeyRange.bound(keyPrefix, keyPrefix + "\uFFFF"));
|
|
17
17
|
let found = false;
|
|
18
18
|
req.onsuccess = () => {
|
|
19
19
|
const cursor = req.result;
|
|
@@ -35,7 +35,7 @@ class IndexedDbVirtualFs {
|
|
|
35
35
|
async listChildren(prefix) {
|
|
36
36
|
return this._db.withStore(this._storeName, "readonly", async (store) => {
|
|
37
37
|
return new Promise((resolve, reject) => {
|
|
38
|
-
const req = store.openCursor();
|
|
38
|
+
const req = store.openCursor(IDBKeyRange.bound(prefix, prefix + "\uFFFF"));
|
|
39
39
|
const map = /* @__PURE__ */ new Map();
|
|
40
40
|
req.onsuccess = () => {
|
|
41
41
|
const cursor = req.result;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/utils/IndexedDbVirtualFs.ts"],
|
|
4
|
-
"mappings": "AAOO,MAAM,mBAAmB;AAAA,EAC9B,YACmB,KACA,YACA,WACjB;AAHiB;AACA;AACA;AAAA,EAChB;AAAA,EAEH,MAAM,SAAS,SAAsD;AACnE,WAAO,KAAK,IAAI,IAAoB,KAAK,YAAY,OAAO;AAAA,EAC9D;AAAA,EAEA,MAAM,SAAS,SAAiB,MAAsB,YAAoC;AACxF,UAAM,KAAK,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC,KAAK,SAAS,GAAG,SAAS,MAAM,WAAW,CAAC;AAAA,EACrF;AAAA,EAEA,MAAM,eAAe,WAAqC;AACxD,WAAO,KAAK,IAAI,UAAU,KAAK,YAAY,aAAa,OAAO,UAAU;AACvE,aAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,cAAM,MAAM,MAAM,WAAW;
|
|
4
|
+
"mappings": "AAOO,MAAM,mBAAmB;AAAA,EAC9B,YACmB,KACA,YACA,WACjB;AAHiB;AACA;AACA;AAAA,EAChB;AAAA,EAEH,MAAM,SAAS,SAAsD;AACnE,WAAO,KAAK,IAAI,IAAoB,KAAK,YAAY,OAAO;AAAA,EAC9D;AAAA,EAEA,MAAM,SAAS,SAAiB,MAAsB,YAAoC;AACxF,UAAM,KAAK,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC,KAAK,SAAS,GAAG,SAAS,MAAM,WAAW,CAAC;AAAA,EACrF;AAAA,EAEA,MAAM,eAAe,WAAqC;AACxD,WAAO,KAAK,IAAI,UAAU,KAAK,YAAY,aAAa,OAAO,UAAU;AACvE,aAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,cAAM,MAAM,MAAM,WAAW,YAAY,MAAM,WAAW,YAAY,QAAQ,CAAC;AAC/E,YAAI,QAAQ;AACZ,YAAI,YAAY,MAAM;AACpB,gBAAM,SAAS,IAAI;AACnB,cAAI,CAAC,QAAQ;AACX,oBAAQ,KAAK;AACb;AAAA,UACF;AACA,gBAAM,MAAM,OAAO,OAAO,GAAG;AAC7B,cAAI,QAAQ,aAAa,IAAI,WAAW,YAAY,GAAG,GAAG;AACxD,oBAAQ;AACR,mBAAO,OAAO;AAAA,UAChB;AACA,iBAAO,SAAS;AAAA,QAClB;AACA,YAAI,UAAU,MAAM,OAAO,IAAI,KAAK;AAAA,MACtC,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,aAAa,QAAmE;AACpF,WAAO,KAAK,IAAI,UAAU,KAAK,YAAY,YAAY,OAAO,UAAU;AACtE,aAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,cAAM,MAAM,MAAM,WAAW,YAAY,MAAM,QAAQ,SAAS,QAAQ,CAAC;AACzE,cAAM,MAAM,oBAAI,IAAqB;AACrC,YAAI,YAAY,MAAM;AACpB,gBAAM,SAAS,IAAI;AACnB,cAAI,CAAC,QAAQ;AACX;AAAA,cACE,MAAM,KAAK,IAAI,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,MAAM,WAAW,OAAO,EAAE,MAAM,YAAY,EAAE;AAAA,YAChF;AACA;AAAA,UACF;AACA,gBAAM,MAAM,OAAO,OAAO,GAAG;AAC7B,cAAI,IAAI,WAAW,MAAM,GAAG;AAC1B,kBAAM,OAAO,IAAI,MAAM,OAAO,MAAM;AACpC,gBAAI,MAAM;AACR,oBAAM,WAAW,KAAK,MAAM,GAAG,EAAE,OAAO,OAAO;AAC/C,kBAAI,SAAS,SAAS,GAAG;AACvB,sBAAM,WAAW,SAAS,CAAC;AAC3B,oBAAI,CAAC,IAAI,IAAI,QAAQ,GAAG;AACtB,wBAAM,QACJ,SAAS,SAAS,KAAM,OAAO,MAAyB,SAAS;AACnE,sBAAI,IAAI,UAAU,KAAK;AAAA,gBACzB;AAAA,cACF;AAAA,YACF;AAAA,UACF;AACA,iBAAO,SAAS;AAAA,QAClB;AACA,YAAI,UAAU,MAAM,OAAO,IAAI,KAAK;AAAA,MACtC,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,UACJ,gBACA,SACe;AACf,QAAI,YAAY,KAAK;AACnB,YAAM,KAAK,SAAS,eAAe,GAAG,GAAG,KAAK;AAC9C;AAAA,IACF;AACA,UAAM,WAAW,QAAQ,MAAM,GAAG,EAAE,OAAO,OAAO;AAClD,QAAI,MAAM;AACV,eAAW,OAAO,UAAU;AAC1B,aAAO,MAAM;AACb,YAAM,WAAW,MAAM,KAAK,SAAS,eAAe,GAAG,CAAC;AACxD,UAAI,CAAC,UAAU;AACb,cAAM,KAAK,SAAS,eAAe,GAAG,GAAG,KAAK;AAAA,MAChD;AAAA,IACF;AAAA,EACF;AACF;",
|
|
5
5
|
"names": []
|
|
6
6
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simplysm/core-browser",
|
|
3
|
-
"version": "13.0.
|
|
3
|
+
"version": "13.0.86",
|
|
4
4
|
"description": "Simplysm package - Core module (browser)",
|
|
5
5
|
"author": "simplysm",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
],
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"tabbable": "^6.4.0",
|
|
27
|
-
"@simplysm/core-common": "13.0.
|
|
27
|
+
"@simplysm/core-common": "13.0.86"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"happy-dom": "^20.8.
|
|
30
|
+
"happy-dom": "^20.8.4"
|
|
31
31
|
}
|
|
32
32
|
}
|
|
@@ -157,10 +157,10 @@ export function copyElement(event: ClipboardEvent): void {
|
|
|
157
157
|
const target = event.target;
|
|
158
158
|
if (clipboardData == null || !(target instanceof Element)) return;
|
|
159
159
|
|
|
160
|
-
const firstInputEl = target.
|
|
160
|
+
const firstInputEl = target.findFirst<HTMLInputElement | HTMLTextAreaElement>(
|
|
161
161
|
"input, textarea",
|
|
162
162
|
);
|
|
163
|
-
if (firstInputEl
|
|
163
|
+
if (firstInputEl !== undefined) {
|
|
164
164
|
clipboardData.setData("text/plain", firstInputEl.value);
|
|
165
165
|
event.preventDefault();
|
|
166
166
|
}
|