@simplysm/core-browser 14.0.1 → 14.0.5
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 +151 -0
- package/docs/download.md +16 -0
- package/docs/element-extensions.md +158 -0
- package/docs/fetch.md +39 -0
- package/docs/file-dialog.md +21 -0
- package/docs/html-element-extensions.md +50 -0
- package/docs/indexed-db-store.md +103 -0
- package/docs/indexed-db-virtual-fs.md +107 -0
- package/package.json +4 -3
package/README.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# @simplysm/core-browser
|
|
2
|
+
|
|
3
|
+
Browser-specific core utilities for the Simplysm framework. Provides DOM element extensions, download/fetch helpers, file dialog, and IndexedDB abstractions.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @simplysm/core-browser
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## API Overview
|
|
12
|
+
|
|
13
|
+
### Element Extensions
|
|
14
|
+
|
|
15
|
+
| API | Type | Description |
|
|
16
|
+
|-----|------|-------------|
|
|
17
|
+
| `ElementBounds` | interface | Bounding rect info for an element |
|
|
18
|
+
| `Element.prototype.findAll` | method | Find all descendant elements matching a CSS selector |
|
|
19
|
+
| `Element.prototype.findFirst` | method | Find the first descendant element matching a CSS selector |
|
|
20
|
+
| `Element.prototype.prependChild` | method | Insert a child as the first element child |
|
|
21
|
+
| `Element.prototype.getParents` | method | Get all ancestor elements (nearest first) |
|
|
22
|
+
| `Element.prototype.findFocusableParent` | method | Find the nearest focusable ancestor |
|
|
23
|
+
| `Element.prototype.findFirstFocusableChild` | method | Find the first focusable descendant |
|
|
24
|
+
| `Element.prototype.isOffsetElement` | method | Check if element is an offset parent |
|
|
25
|
+
| `Element.prototype.isVisible` | method | Check if element is visible on screen |
|
|
26
|
+
| `copyElement` | function | Copy element content to clipboard via ClipboardEvent |
|
|
27
|
+
| `pasteToElement` | function | Paste clipboard content into element via ClipboardEvent |
|
|
28
|
+
| `getBounds` | function | Get bounding info for multiple elements using IntersectionObserver |
|
|
29
|
+
|
|
30
|
+
> See [docs/element-extensions.md](./docs/element-extensions.md) for details.
|
|
31
|
+
|
|
32
|
+
### HTML Element Extensions
|
|
33
|
+
|
|
34
|
+
| API | Type | Description |
|
|
35
|
+
|-----|------|-------------|
|
|
36
|
+
| `HTMLElement.prototype.repaint` | method | Force a synchronous repaint |
|
|
37
|
+
| `HTMLElement.prototype.getRelativeOffset` | method | Calculate position relative to a parent element |
|
|
38
|
+
| `HTMLElement.prototype.scrollIntoViewIfNeeded` | method | Scroll container so target is not hidden by offset areas |
|
|
39
|
+
|
|
40
|
+
> See [docs/html-element-extensions.md](./docs/html-element-extensions.md) for details.
|
|
41
|
+
|
|
42
|
+
### Download
|
|
43
|
+
|
|
44
|
+
| API | Type | Description |
|
|
45
|
+
|-----|------|-------------|
|
|
46
|
+
| `downloadBlob` | function | Download a Blob as a file |
|
|
47
|
+
|
|
48
|
+
> See [docs/download.md](./docs/download.md) for details.
|
|
49
|
+
|
|
50
|
+
### Fetch
|
|
51
|
+
|
|
52
|
+
| API | Type | Description |
|
|
53
|
+
|-----|------|-------------|
|
|
54
|
+
| `DownloadProgress` | interface | Progress info for fetch downloads |
|
|
55
|
+
| `fetchUrlBytes` | function | Download binary data from a URL with progress callback |
|
|
56
|
+
|
|
57
|
+
> See [docs/fetch.md](./docs/fetch.md) for details.
|
|
58
|
+
|
|
59
|
+
### File Dialog
|
|
60
|
+
|
|
61
|
+
| API | Type | Description |
|
|
62
|
+
|-----|------|-------------|
|
|
63
|
+
| `openFileDialog` | function | Programmatically open a file picker dialog |
|
|
64
|
+
|
|
65
|
+
> See [docs/file-dialog.md](./docs/file-dialog.md) for details.
|
|
66
|
+
|
|
67
|
+
### IndexedDB Store
|
|
68
|
+
|
|
69
|
+
| API | Type | Description |
|
|
70
|
+
|-----|------|-------------|
|
|
71
|
+
| `StoreConfig` | interface | Configuration for an IndexedDB object store |
|
|
72
|
+
| `IndexedDbStore` | class | Generic IndexedDB wrapper with CRUD operations |
|
|
73
|
+
|
|
74
|
+
> See [docs/indexed-db-store.md](./docs/indexed-db-store.md) for details.
|
|
75
|
+
|
|
76
|
+
### IndexedDB Virtual FS
|
|
77
|
+
|
|
78
|
+
| API | Type | Description |
|
|
79
|
+
|-----|------|-------------|
|
|
80
|
+
| `VirtualFsEntry` | interface | Entry representing a file or directory in the virtual FS |
|
|
81
|
+
| `IndexedDbVirtualFs` | class | Virtual file system backed by IndexedDB |
|
|
82
|
+
|
|
83
|
+
> See [docs/indexed-db-virtual-fs.md](./docs/indexed-db-virtual-fs.md) for details.
|
|
84
|
+
|
|
85
|
+
## Usage Examples
|
|
86
|
+
|
|
87
|
+
### Copy/Paste with ClipboardEvent
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { copyElement, pasteToElement } from "@simplysm/core-browser";
|
|
91
|
+
|
|
92
|
+
document.addEventListener("copy", (event) => copyElement(event));
|
|
93
|
+
document.addEventListener("paste", (event) => pasteToElement(event));
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Download a Blob
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
import { downloadBlob } from "@simplysm/core-browser";
|
|
100
|
+
|
|
101
|
+
const blob = new Blob(["hello"], { type: "text/plain" });
|
|
102
|
+
downloadBlob(blob, "hello.txt");
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Fetch binary data with progress
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
import { fetchUrlBytes } from "@simplysm/core-browser";
|
|
109
|
+
|
|
110
|
+
const data = await fetchUrlBytes("/api/file.bin", {
|
|
111
|
+
onProgress: ({ receivedLength, contentLength }) => {
|
|
112
|
+
console.log(`${receivedLength} / ${contentLength}`);
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Open a file dialog
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
import { openFileDialog } from "@simplysm/core-browser";
|
|
121
|
+
|
|
122
|
+
const files = await openFileDialog({ accept: ".csv", multiple: true });
|
|
123
|
+
if (files) {
|
|
124
|
+
for (const file of files) {
|
|
125
|
+
console.log(file.name);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### IndexedDB Store
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
import { IndexedDbStore } from "@simplysm/core-browser";
|
|
134
|
+
|
|
135
|
+
const store = new IndexedDbStore("myDb", 1, [{ name: "items", keyPath: "id" }]);
|
|
136
|
+
await store.put("items", { id: "1", value: "hello" });
|
|
137
|
+
const item = await store.get<{ id: string; value: string }>("items", "1");
|
|
138
|
+
store.close();
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Get element bounds
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
import { getBounds } from "@simplysm/core-browser";
|
|
145
|
+
|
|
146
|
+
const elements = document.querySelectorAll(".card");
|
|
147
|
+
const bounds = await getBounds([...elements]);
|
|
148
|
+
for (const b of bounds) {
|
|
149
|
+
console.log(`${b.target.tagName}: ${b.top}, ${b.left}, ${b.width}x${b.height}`);
|
|
150
|
+
}
|
|
151
|
+
```
|
package/docs/download.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Download
|
|
2
|
+
|
|
3
|
+
Utility for triggering file downloads in the browser.
|
|
4
|
+
|
|
5
|
+
## `downloadBlob`
|
|
6
|
+
|
|
7
|
+
Download a `Blob` as a file by creating a temporary object URL and clicking a hidden anchor element. The object URL is revoked after 1 second.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
function downloadBlob(blob: Blob, fileName: string): void
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
| Parameter | Type | Description |
|
|
14
|
+
|-----------|------|-------------|
|
|
15
|
+
| `blob` | `Blob` | The Blob to download |
|
|
16
|
+
| `fileName` | `string` | File name for the download |
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Element Extensions
|
|
2
|
+
|
|
3
|
+
Side-effect import that extends the global `Element` prototype with DOM utility methods, plus standalone clipboard and bounds functions.
|
|
4
|
+
|
|
5
|
+
## `ElementBounds`
|
|
6
|
+
|
|
7
|
+
Bounding rectangle information for an element, returned by `getBounds`.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
interface ElementBounds {
|
|
11
|
+
target: Element;
|
|
12
|
+
top: number;
|
|
13
|
+
left: number;
|
|
14
|
+
width: number;
|
|
15
|
+
height: number;
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
| Field | Type | Description |
|
|
20
|
+
|-------|------|-------------|
|
|
21
|
+
| `target` | `Element` | The measured element |
|
|
22
|
+
| `top` | `number` | Top position relative to viewport |
|
|
23
|
+
| `left` | `number` | Left position relative to viewport |
|
|
24
|
+
| `width` | `number` | Element width |
|
|
25
|
+
| `height` | `number` | Element height |
|
|
26
|
+
|
|
27
|
+
## `Element.prototype.findAll`
|
|
28
|
+
|
|
29
|
+
Find all descendant elements matching a CSS selector.
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
findAll<TEl extends Element = Element>(selector: string): TEl[]
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
| Parameter | Type | Description |
|
|
36
|
+
|-----------|------|-------------|
|
|
37
|
+
| `selector` | `string` | CSS selector. Returns empty array if empty string. |
|
|
38
|
+
|
|
39
|
+
**Returns:** `TEl[]` -- Array of matching elements.
|
|
40
|
+
|
|
41
|
+
## `Element.prototype.findFirst`
|
|
42
|
+
|
|
43
|
+
Find the first descendant element matching a CSS selector.
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
findFirst<TEl extends Element = Element>(selector: string): TEl | undefined
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
| Parameter | Type | Description |
|
|
50
|
+
|-----------|------|-------------|
|
|
51
|
+
| `selector` | `string` | CSS selector. Returns `undefined` if empty string. |
|
|
52
|
+
|
|
53
|
+
**Returns:** `TEl | undefined` -- First matching element or `undefined`.
|
|
54
|
+
|
|
55
|
+
## `Element.prototype.prependChild`
|
|
56
|
+
|
|
57
|
+
Insert a child element as the first child.
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
prependChild<TEl extends Element>(child: TEl): TEl
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
| Parameter | Type | Description |
|
|
64
|
+
|-----------|------|-------------|
|
|
65
|
+
| `child` | `TEl` | Element to insert |
|
|
66
|
+
|
|
67
|
+
**Returns:** `TEl` -- The inserted child element.
|
|
68
|
+
|
|
69
|
+
## `Element.prototype.getParents`
|
|
70
|
+
|
|
71
|
+
Get all ancestor elements, ordered from nearest to farthest.
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
getParents(): Element[]
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Returns:** `Element[]` -- Array of parent elements (nearest first).
|
|
78
|
+
|
|
79
|
+
## `Element.prototype.findFocusableParent`
|
|
80
|
+
|
|
81
|
+
Find the nearest focusable ancestor element (uses `tabbable` library).
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
findFocusableParent(): HTMLElement | undefined
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Returns:** `HTMLElement | undefined` -- First focusable parent or `undefined`.
|
|
88
|
+
|
|
89
|
+
## `Element.prototype.findFirstFocusableChild`
|
|
90
|
+
|
|
91
|
+
Find the first focusable descendant element (uses `tabbable` library).
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
findFirstFocusableChild(): HTMLElement | undefined
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Returns:** `HTMLElement | undefined` -- First focusable child or `undefined`.
|
|
98
|
+
|
|
99
|
+
## `Element.prototype.isOffsetElement`
|
|
100
|
+
|
|
101
|
+
Check whether the element is an offset parent (`position: relative | absolute | fixed | sticky`).
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
isOffsetElement(): boolean
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Returns:** `boolean` -- `true` if element has a positioning CSS property.
|
|
108
|
+
|
|
109
|
+
## `Element.prototype.isVisible`
|
|
110
|
+
|
|
111
|
+
Check whether the element is visible on screen. Checks `getClientRects()`, `visibility`, and `opacity`.
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
isVisible(): boolean
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Returns:** `boolean` -- `true` if the element is visible.
|
|
118
|
+
|
|
119
|
+
## `copyElement`
|
|
120
|
+
|
|
121
|
+
Copy element content to the clipboard. Intended for use as a `copy` event handler. Finds the first `input` or `textarea` within the event target and copies its value.
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
function copyElement(event: ClipboardEvent): void
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
| Parameter | Type | Description |
|
|
128
|
+
|-----------|------|-------------|
|
|
129
|
+
| `event` | `ClipboardEvent` | The copy event object |
|
|
130
|
+
|
|
131
|
+
## `pasteToElement`
|
|
132
|
+
|
|
133
|
+
Paste clipboard content into an element. Intended for use as a `paste` event handler. Finds the first `input` or `textarea` within the event target and replaces its entire value with the clipboard text.
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
function pasteToElement(event: ClipboardEvent): void
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
| Parameter | Type | Description |
|
|
140
|
+
|-----------|------|-------------|
|
|
141
|
+
| `event` | `ClipboardEvent` | The paste event object |
|
|
142
|
+
|
|
143
|
+
## `getBounds`
|
|
144
|
+
|
|
145
|
+
Get bounding rectangle information for multiple elements using `IntersectionObserver`. Results are returned in the same order as the input array. Duplicate elements are deduplicated.
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
async function getBounds(els: Element[], timeout?: number): Promise<ElementBounds[]>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
| Parameter | Type | Default | Description |
|
|
152
|
+
|-----------|------|---------|-------------|
|
|
153
|
+
| `els` | `Element[]` | | Target elements |
|
|
154
|
+
| `timeout` | `number` | `5000` | Timeout in milliseconds |
|
|
155
|
+
|
|
156
|
+
**Returns:** `Promise<ElementBounds[]>` -- Bounding info sorted by input order.
|
|
157
|
+
|
|
158
|
+
**Throws:** `TimeoutError` if the observer does not respond within the timeout.
|
package/docs/fetch.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Fetch
|
|
2
|
+
|
|
3
|
+
Utility for downloading binary data from a URL with optional progress tracking.
|
|
4
|
+
|
|
5
|
+
## `DownloadProgress`
|
|
6
|
+
|
|
7
|
+
Progress information emitted during a fetch download.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
interface DownloadProgress {
|
|
11
|
+
receivedLength: number;
|
|
12
|
+
contentLength: number;
|
|
13
|
+
}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
| Field | Type | Description |
|
|
17
|
+
|-------|------|-------------|
|
|
18
|
+
| `receivedLength` | `number` | Bytes received so far |
|
|
19
|
+
| `contentLength` | `number` | Total content length from the `Content-Length` header |
|
|
20
|
+
|
|
21
|
+
## `fetchUrlBytes`
|
|
22
|
+
|
|
23
|
+
Download binary data from a URL. Supports progress callbacks via `ReadableStream` reader. When the `Content-Length` header is available, the result buffer is pre-allocated for efficiency. When unavailable (chunked encoding), chunks are collected and concatenated.
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
async function fetchUrlBytes(
|
|
27
|
+
url: string,
|
|
28
|
+
options?: { onProgress?: (progress: DownloadProgress) => void },
|
|
29
|
+
): Promise<Uint8Array>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
| Parameter | Type | Description |
|
|
33
|
+
|-----------|------|-------------|
|
|
34
|
+
| `url` | `string` | URL to fetch |
|
|
35
|
+
| `options.onProgress` | `(progress: DownloadProgress) => void` | Optional progress callback |
|
|
36
|
+
|
|
37
|
+
**Returns:** `Promise<Uint8Array>` -- The downloaded binary data.
|
|
38
|
+
|
|
39
|
+
**Throws:** `Error` if the response is not OK or the body cannot be read.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# File Dialog
|
|
2
|
+
|
|
3
|
+
Utility for programmatically opening a native file picker dialog.
|
|
4
|
+
|
|
5
|
+
## `openFileDialog`
|
|
6
|
+
|
|
7
|
+
Open a file selection dialog by creating and clicking a hidden `<input type="file">` element. Returns the selected files or `undefined` if the dialog is cancelled.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
function openFileDialog(options?: {
|
|
11
|
+
accept?: string;
|
|
12
|
+
multiple?: boolean;
|
|
13
|
+
}): Promise<File[] | undefined>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
| Parameter | Type | Default | Description |
|
|
17
|
+
|-----------|------|---------|-------------|
|
|
18
|
+
| `options.accept` | `string` | `undefined` | File type filter (e.g., `".csv"`, `"image/*"`) |
|
|
19
|
+
| `options.multiple` | `boolean` | `false` | Allow selecting multiple files |
|
|
20
|
+
|
|
21
|
+
**Returns:** `Promise<File[] | undefined>` -- Selected files array, or `undefined` if cancelled or no files selected.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# HTML Element Extensions
|
|
2
|
+
|
|
3
|
+
Side-effect import that extends the global `HTMLElement` prototype with layout and scroll utility methods.
|
|
4
|
+
|
|
5
|
+
## `HTMLElement.prototype.repaint`
|
|
6
|
+
|
|
7
|
+
Force a synchronous repaint by triggering a reflow (reads `offsetHeight`).
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
repaint(): void
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## `HTMLElement.prototype.getRelativeOffset`
|
|
14
|
+
|
|
15
|
+
Calculate the element's position relative to a parent element. Returns document-based coordinates (including `window.scrollX/Y`) suitable for CSS `top`/`left` properties.
|
|
16
|
+
|
|
17
|
+
The calculation accounts for:
|
|
18
|
+
- Viewport position (`getBoundingClientRect`)
|
|
19
|
+
- Document scroll (`window.scrollX/Y`)
|
|
20
|
+
- Parent internal scroll (`parentEl.scrollTop/Left`)
|
|
21
|
+
- Intermediate element border widths
|
|
22
|
+
- CSS `transform`
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
getRelativeOffset(parent: HTMLElement | string): { top: number; left: number }
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
| Parameter | Type | Description |
|
|
29
|
+
|-----------|------|-------------|
|
|
30
|
+
| `parent` | `HTMLElement \| string` | Parent element or CSS selector to match via `closest()` |
|
|
31
|
+
|
|
32
|
+
**Returns:** `{ top: number; left: number }` -- Coordinates usable for CSS `top`/`left`.
|
|
33
|
+
|
|
34
|
+
**Throws:** `ArgumentError` if the parent element cannot be found.
|
|
35
|
+
|
|
36
|
+
## `HTMLElement.prototype.scrollIntoViewIfNeeded`
|
|
37
|
+
|
|
38
|
+
Scroll the container so that the target position is not obscured by fixed offset areas (e.g., sticky headers or fixed columns). Only handles cases where the target is above or to the left of the visible area; downward/rightward scrolling relies on the browser's default focus scroll behavior.
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
scrollIntoViewIfNeeded(
|
|
42
|
+
target: { top: number; left: number },
|
|
43
|
+
offset?: { top: number; left: number },
|
|
44
|
+
): void
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
| Parameter | Type | Default | Description |
|
|
48
|
+
|-----------|------|---------|-------------|
|
|
49
|
+
| `target` | `{ top: number; left: number }` | | Target position within the container (`offsetTop`, `offsetLeft`) |
|
|
50
|
+
| `offset` | `{ top: number; left: number }` | `{ top: 0, left: 0 }` | Size of the offset area that must not obscure the target (e.g., fixed header height) |
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# IndexedDB Store
|
|
2
|
+
|
|
3
|
+
Generic IndexedDB wrapper providing typed CRUD operations and transaction management.
|
|
4
|
+
|
|
5
|
+
## `StoreConfig`
|
|
6
|
+
|
|
7
|
+
Configuration for an IndexedDB object store, used when opening the database.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
interface StoreConfig {
|
|
11
|
+
name: string;
|
|
12
|
+
keyPath: string;
|
|
13
|
+
}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
| Field | Type | Description |
|
|
17
|
+
|-------|------|-------------|
|
|
18
|
+
| `name` | `string` | Object store name |
|
|
19
|
+
| `keyPath` | `string` | Key path for the object store |
|
|
20
|
+
|
|
21
|
+
## `IndexedDbStore`
|
|
22
|
+
|
|
23
|
+
A wrapper around the IndexedDB API with automatic database opening, version upgrade handling, and typed CRUD methods.
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
class IndexedDbStore {
|
|
27
|
+
constructor(dbName: string, dbVersion: number, storeConfigs: StoreConfig[]);
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
| Constructor Parameter | Type | Description |
|
|
32
|
+
|----------------------|------|-------------|
|
|
33
|
+
| `dbName` | `string` | Database name |
|
|
34
|
+
| `dbVersion` | `number` | Database version (triggers `onupgradeneeded` when increased) |
|
|
35
|
+
| `storeConfigs` | `StoreConfig[]` | Object store configurations to create on upgrade |
|
|
36
|
+
|
|
37
|
+
### Methods
|
|
38
|
+
|
|
39
|
+
#### `open`
|
|
40
|
+
|
|
41
|
+
Open the database (creates stores on version upgrade). Returns the existing connection if already open. Concurrent calls return the same pending promise.
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
async open(): Promise<IDBDatabase>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
#### `withStore`
|
|
48
|
+
|
|
49
|
+
Execute a function within a transaction on a specific store. Handles transaction commit/abort automatically.
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
async withStore<TResult>(
|
|
53
|
+
storeName: string,
|
|
54
|
+
mode: IDBTransactionMode,
|
|
55
|
+
fn: (store: IDBObjectStore) => Promise<TResult>,
|
|
56
|
+
): Promise<TResult>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
| Parameter | Type | Description |
|
|
60
|
+
|-----------|------|-------------|
|
|
61
|
+
| `storeName` | `string` | Target object store name |
|
|
62
|
+
| `mode` | `IDBTransactionMode` | `"readonly"` or `"readwrite"` |
|
|
63
|
+
| `fn` | `(store: IDBObjectStore) => Promise<TResult>` | Function to execute within the transaction |
|
|
64
|
+
|
|
65
|
+
#### `get`
|
|
66
|
+
|
|
67
|
+
Get a single value by key.
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
async get<TValue>(storeName: string, key: IDBValidKey): Promise<TValue | undefined>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
#### `put`
|
|
74
|
+
|
|
75
|
+
Insert or update a value.
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
async put(storeName: string, value: unknown): Promise<void>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
#### `delete`
|
|
82
|
+
|
|
83
|
+
Delete a value by key.
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
async delete(storeName: string, key: IDBValidKey): Promise<void>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### `getAll`
|
|
90
|
+
|
|
91
|
+
Get all values from a store.
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
async getAll<TItem>(storeName: string): Promise<TItem[]>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
#### `close`
|
|
98
|
+
|
|
99
|
+
Close the database connection.
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
close(): void
|
|
103
|
+
```
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# IndexedDB Virtual FS
|
|
2
|
+
|
|
3
|
+
A virtual file system abstraction backed by IndexedDB, built on top of `IndexedDbStore`.
|
|
4
|
+
|
|
5
|
+
## `VirtualFsEntry`
|
|
6
|
+
|
|
7
|
+
Represents a file or directory entry in the virtual file system.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
interface VirtualFsEntry {
|
|
11
|
+
kind: "file" | "dir";
|
|
12
|
+
dataBase64?: string;
|
|
13
|
+
}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
| Field | Type | Description |
|
|
17
|
+
|-------|------|-------------|
|
|
18
|
+
| `kind` | `"file" \| "dir"` | Entry type |
|
|
19
|
+
| `dataBase64` | `string \| undefined` | Base64-encoded file data (only for files) |
|
|
20
|
+
|
|
21
|
+
## `IndexedDbVirtualFs`
|
|
22
|
+
|
|
23
|
+
Virtual file system that stores entries in an `IndexedDbStore`. Supports hierarchical path-based operations including directory creation, prefix-based deletion, and child listing.
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
class IndexedDbVirtualFs {
|
|
27
|
+
constructor(db: IndexedDbStore, storeName: string, keyField: string);
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
| Constructor Parameter | Type | Description |
|
|
32
|
+
|----------------------|------|-------------|
|
|
33
|
+
| `db` | `IndexedDbStore` | The IndexedDB store instance |
|
|
34
|
+
| `storeName` | `string` | Object store name to use |
|
|
35
|
+
| `keyField` | `string` | Field name used as the key in the store |
|
|
36
|
+
|
|
37
|
+
### Methods
|
|
38
|
+
|
|
39
|
+
#### `getEntry`
|
|
40
|
+
|
|
41
|
+
Get a virtual FS entry by its full key.
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
async getEntry(fullKey: string): Promise<VirtualFsEntry | undefined>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
| Parameter | Type | Description |
|
|
48
|
+
|-----------|------|-------------|
|
|
49
|
+
| `fullKey` | `string` | Full key identifying the entry |
|
|
50
|
+
|
|
51
|
+
#### `putEntry`
|
|
52
|
+
|
|
53
|
+
Insert or update a virtual FS entry.
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
async putEntry(fullKey: string, kind: "file" | "dir", dataBase64?: string): Promise<void>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
| Parameter | Type | Description |
|
|
60
|
+
|-----------|------|-------------|
|
|
61
|
+
| `fullKey` | `string` | Full key for the entry |
|
|
62
|
+
| `kind` | `"file" \| "dir"` | Entry type |
|
|
63
|
+
| `dataBase64` | `string` | Optional base64-encoded data (for files) |
|
|
64
|
+
|
|
65
|
+
#### `deleteByPrefix`
|
|
66
|
+
|
|
67
|
+
Delete all entries whose key matches the given prefix or starts with `prefix + "/"`.
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
async deleteByPrefix(keyPrefix: string): Promise<boolean>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
| Parameter | Type | Description |
|
|
74
|
+
|-----------|------|-------------|
|
|
75
|
+
| `keyPrefix` | `string` | Key prefix to match |
|
|
76
|
+
|
|
77
|
+
**Returns:** `Promise<boolean>` -- `true` if at least one entry was deleted.
|
|
78
|
+
|
|
79
|
+
#### `listChildren`
|
|
80
|
+
|
|
81
|
+
List immediate children under a given prefix. Returns each child's name and whether it is a directory.
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
async listChildren(prefix: string): Promise<{ name: string; isDirectory: boolean }[]>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
| Parameter | Type | Description |
|
|
88
|
+
|-----------|------|-------------|
|
|
89
|
+
| `prefix` | `string` | Parent path prefix (e.g., `"/root/dir/"`) |
|
|
90
|
+
|
|
91
|
+
**Returns:** `Promise<{ name: string; isDirectory: boolean }[]>` -- Array of child entries.
|
|
92
|
+
|
|
93
|
+
#### `ensureDir`
|
|
94
|
+
|
|
95
|
+
Ensure all directories along a path exist, creating missing ones.
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
async ensureDir(
|
|
99
|
+
fullKeyBuilder: (path: string) => string,
|
|
100
|
+
dirPath: string,
|
|
101
|
+
): Promise<void>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
| Parameter | Type | Description |
|
|
105
|
+
|-----------|------|-------------|
|
|
106
|
+
| `fullKeyBuilder` | `(path: string) => string` | Function to build the full key from a path segment |
|
|
107
|
+
| `dirPath` | `string` | Directory path to ensure (e.g., `"/a/b/c"`) |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simplysm/core-browser",
|
|
3
|
-
"version": "14.0.
|
|
3
|
+
"version": "14.0.5",
|
|
4
4
|
"description": "심플리즘 패키지 - 코어 (browser)",
|
|
5
5
|
"author": "심플리즘",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"types": "./dist/index.d.ts",
|
|
15
15
|
"files": [
|
|
16
16
|
"dist",
|
|
17
|
-
"src"
|
|
17
|
+
"src",
|
|
18
|
+
"docs"
|
|
18
19
|
],
|
|
19
20
|
"sideEffects": [
|
|
20
21
|
"./src/extensions/element-ext.ts",
|
|
@@ -24,6 +25,6 @@
|
|
|
24
25
|
],
|
|
25
26
|
"dependencies": {
|
|
26
27
|
"tabbable": "^6.4.0",
|
|
27
|
-
"@simplysm/core-common": "14.0.
|
|
28
|
+
"@simplysm/core-common": "14.0.5"
|
|
28
29
|
}
|
|
29
30
|
}
|