web-a2e 1.0.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.
- package/.clangd +5 -0
- package/.mcp.json +12 -0
- package/CLAUDE.md +362 -0
- package/CMakeLists.txt +774 -0
- package/LICENSE +21 -0
- package/README.md +392 -0
- package/build-wasm/generated/roms.cpp +2447 -0
- package/docker-compose.staging.yml +9 -0
- package/docs/basic-rom-disassembly.md +6663 -0
- package/docs/softswitch-comparison.md +273 -0
- package/docs/thunderclock-debug.md +89 -0
- package/examples/cube.bas +72 -0
- package/examples/hello.s +55 -0
- package/examples/scroll.s +140 -0
- package/package.json +18 -0
- package/public/assets/apple-logo-old.png +0 -0
- package/public/assets/apple-logo.png +0 -0
- package/public/assets/drive-closed-light-on.png +0 -0
- package/public/assets/drive-closed.png +0 -0
- package/public/assets/drive-open-light-on.png +0 -0
- package/public/assets/drive-open.png +0 -0
- package/public/audio-worklet.js +82 -0
- package/public/disks/Apple DOS 3.3 January 1983.dsk +0 -0
- package/public/disks/ProDOS 2.4.3.po +0 -0
- package/public/disks/h32mb.2mg +0 -0
- package/public/disks/library.json +26 -0
- package/public/docs/llms/llm-assembler.txt +90 -0
- package/public/docs/llms/llm-basic-program.txt +256 -0
- package/public/docs/llms/llm-disk-drives.txt +72 -0
- package/public/docs/llms/llm-file-explorer.txt +50 -0
- package/public/docs/llms/llm-hard-drives.txt +80 -0
- package/public/docs/llms/llm-main.txt +51 -0
- package/public/docs/llms/llm-slot-configuration.txt +66 -0
- package/public/icons/icon-192.svg +4 -0
- package/public/icons/icon-512.svg +4 -0
- package/public/index.html +661 -0
- package/public/llms.txt +49 -0
- package/public/manifest.json +29 -0
- package/public/shaders/burnin.glsl +22 -0
- package/public/shaders/crt.glsl +706 -0
- package/public/shaders/edge.glsl +109 -0
- package/public/shaders/vertex.glsl +8 -0
- package/public/sw.js +186 -0
- package/roms/341-0027.bin +0 -0
- package/roms/341-0160-A-US-UK.bin +0 -0
- package/roms/341-0160-A.bin +0 -0
- package/roms/342-0273-A-US-UK.bin +0 -0
- package/roms/342-0349-B-C0-FF.bin +0 -0
- package/roms/Apple Mouse Interface Card ROM - 342-0270-C.bin +0 -0
- package/roms/Thunderclock Plus ROM.bin +0 -0
- package/scripts/generate_roms.sh +69 -0
- package/src/bindings/wasm_interface.cpp +1940 -0
- package/src/core/assembler/assembler.cpp +1239 -0
- package/src/core/assembler/assembler.hpp +115 -0
- package/src/core/audio/audio.cpp +160 -0
- package/src/core/audio/audio.hpp +81 -0
- package/src/core/basic/basic_detokenizer.cpp +436 -0
- package/src/core/basic/basic_detokenizer.hpp +41 -0
- package/src/core/basic/basic_tokenizer.cpp +286 -0
- package/src/core/basic/basic_tokenizer.hpp +26 -0
- package/src/core/basic/basic_tokens.hpp +295 -0
- package/src/core/cards/disk2_card.cpp +568 -0
- package/src/core/cards/disk2_card.hpp +316 -0
- package/src/core/cards/expansion_card.hpp +185 -0
- package/src/core/cards/mockingboard/ay8910.cpp +616 -0
- package/src/core/cards/mockingboard/ay8910.hpp +159 -0
- package/src/core/cards/mockingboard/via6522.cpp +530 -0
- package/src/core/cards/mockingboard/via6522.hpp +163 -0
- package/src/core/cards/mockingboard_card.cpp +312 -0
- package/src/core/cards/mockingboard_card.hpp +159 -0
- package/src/core/cards/mouse_card.cpp +654 -0
- package/src/core/cards/mouse_card.hpp +190 -0
- package/src/core/cards/smartport/block_device.cpp +202 -0
- package/src/core/cards/smartport/block_device.hpp +60 -0
- package/src/core/cards/smartport/smartport_card.cpp +603 -0
- package/src/core/cards/smartport/smartport_card.hpp +120 -0
- package/src/core/cards/thunderclock_card.cpp +237 -0
- package/src/core/cards/thunderclock_card.hpp +122 -0
- package/src/core/cpu/cpu6502.cpp +1609 -0
- package/src/core/cpu/cpu6502.hpp +203 -0
- package/src/core/debug/condition_evaluator.cpp +470 -0
- package/src/core/debug/condition_evaluator.hpp +87 -0
- package/src/core/disassembler/disassembler.cpp +552 -0
- package/src/core/disassembler/disassembler.hpp +171 -0
- package/src/core/disk-image/disk_image.hpp +267 -0
- package/src/core/disk-image/dsk_disk_image.cpp +827 -0
- package/src/core/disk-image/dsk_disk_image.hpp +204 -0
- package/src/core/disk-image/gcr_encoding.cpp +147 -0
- package/src/core/disk-image/gcr_encoding.hpp +78 -0
- package/src/core/disk-image/woz_disk_image.cpp +1049 -0
- package/src/core/disk-image/woz_disk_image.hpp +343 -0
- package/src/core/emulator.cpp +2126 -0
- package/src/core/emulator.hpp +434 -0
- package/src/core/filesystem/dos33.cpp +178 -0
- package/src/core/filesystem/dos33.hpp +66 -0
- package/src/core/filesystem/pascal.cpp +262 -0
- package/src/core/filesystem/pascal.hpp +87 -0
- package/src/core/filesystem/prodos.cpp +369 -0
- package/src/core/filesystem/prodos.hpp +119 -0
- package/src/core/input/keyboard.cpp +227 -0
- package/src/core/input/keyboard.hpp +111 -0
- package/src/core/mmu/mmu.cpp +1387 -0
- package/src/core/mmu/mmu.hpp +236 -0
- package/src/core/types.hpp +196 -0
- package/src/core/video/video.cpp +680 -0
- package/src/core/video/video.hpp +156 -0
- package/src/css/assembler-editor.css +1617 -0
- package/src/css/base.css +470 -0
- package/src/css/basic-debugger.css +791 -0
- package/src/css/basic-editor.css +792 -0
- package/src/css/controls.css +783 -0
- package/src/css/cpu-debugger.css +1413 -0
- package/src/css/debug-base.css +160 -0
- package/src/css/debug-windows.css +6455 -0
- package/src/css/disk-drives.css +406 -0
- package/src/css/documentation.css +392 -0
- package/src/css/file-explorer.css +867 -0
- package/src/css/hard-drive.css +180 -0
- package/src/css/layout.css +217 -0
- package/src/css/memory-windows.css +798 -0
- package/src/css/modals.css +510 -0
- package/src/css/monitor.css +425 -0
- package/src/css/release-notes.css +101 -0
- package/src/css/responsive.css +400 -0
- package/src/css/rule-builder.css +340 -0
- package/src/css/save-states.css +201 -0
- package/src/css/settings-windows.css +1231 -0
- package/src/css/window-switcher.css +150 -0
- package/src/js/agent/agent-manager.js +643 -0
- package/src/js/agent/agent-tools.js +293 -0
- package/src/js/agent/agent-version-tools.js +131 -0
- package/src/js/agent/assembler-tools.js +357 -0
- package/src/js/agent/basic-program-tools.js +894 -0
- package/src/js/agent/disk-tools.js +417 -0
- package/src/js/agent/file-explorer-tools.js +269 -0
- package/src/js/agent/index.js +13 -0
- package/src/js/agent/main-tools.js +222 -0
- package/src/js/agent/slot-tools.js +303 -0
- package/src/js/agent/smartport-tools.js +257 -0
- package/src/js/agent/window-tools.js +80 -0
- package/src/js/audio/audio-driver.js +417 -0
- package/src/js/audio/audio-worklet.js +85 -0
- package/src/js/audio/index.js +8 -0
- package/src/js/config/default-layout.js +34 -0
- package/src/js/config/version.js +8 -0
- package/src/js/data/apple2-rom-routines.js +577 -0
- package/src/js/debug/assembler-editor-window.js +2993 -0
- package/src/js/debug/basic-breakpoint-manager.js +529 -0
- package/src/js/debug/basic-program-parser.js +436 -0
- package/src/js/debug/basic-program-window.js +2594 -0
- package/src/js/debug/basic-variable-inspector.js +447 -0
- package/src/js/debug/breakpoint-manager.js +472 -0
- package/src/js/debug/cpu-debugger-window.js +2396 -0
- package/src/js/debug/index.js +22 -0
- package/src/js/debug/label-manager.js +238 -0
- package/src/js/debug/memory-browser-window.js +416 -0
- package/src/js/debug/memory-heat-map-window.js +481 -0
- package/src/js/debug/memory-map-window.js +206 -0
- package/src/js/debug/mockingboard-window.js +882 -0
- package/src/js/debug/mouse-card-window.js +355 -0
- package/src/js/debug/rule-builder-window.js +648 -0
- package/src/js/debug/soft-switch-window.js +458 -0
- package/src/js/debug/stack-viewer-window.js +221 -0
- package/src/js/debug/symbols.js +416 -0
- package/src/js/debug/trace-panel.js +291 -0
- package/src/js/debug/zero-page-watch-window.js +297 -0
- package/src/js/disk-manager/disk-drives-window.js +212 -0
- package/src/js/disk-manager/disk-operations.js +284 -0
- package/src/js/disk-manager/disk-persistence.js +301 -0
- package/src/js/disk-manager/disk-surface-renderer.js +388 -0
- package/src/js/disk-manager/drive-sounds.js +139 -0
- package/src/js/disk-manager/hard-drive-manager.js +481 -0
- package/src/js/disk-manager/hard-drive-persistence.js +187 -0
- package/src/js/disk-manager/hard-drive-window.js +57 -0
- package/src/js/disk-manager/index.js +890 -0
- package/src/js/display/display-settings-window.js +383 -0
- package/src/js/display/index.js +10 -0
- package/src/js/display/screen-window.js +342 -0
- package/src/js/display/webgl-renderer.js +705 -0
- package/src/js/file-explorer/disassembler.js +574 -0
- package/src/js/file-explorer/dos33.js +266 -0
- package/src/js/file-explorer/file-viewer.js +359 -0
- package/src/js/file-explorer/index.js +1261 -0
- package/src/js/file-explorer/prodos.js +549 -0
- package/src/js/file-explorer/utils.js +67 -0
- package/src/js/help/documentation-window.js +1096 -0
- package/src/js/help/index.js +10 -0
- package/src/js/help/release-notes-window.js +85 -0
- package/src/js/help/release-notes.js +612 -0
- package/src/js/input/gamepad-handler.js +176 -0
- package/src/js/input/index.js +12 -0
- package/src/js/input/input-handler.js +396 -0
- package/src/js/input/joystick-window.js +404 -0
- package/src/js/input/mouse-handler.js +99 -0
- package/src/js/input/text-selection.js +462 -0
- package/src/js/main.js +653 -0
- package/src/js/state/index.js +15 -0
- package/src/js/state/save-states-window.js +393 -0
- package/src/js/state/state-manager.js +409 -0
- package/src/js/state/state-persistence.js +218 -0
- package/src/js/ui/confirm.js +43 -0
- package/src/js/ui/disk-drive-positioner.js +347 -0
- package/src/js/ui/reminder-controller.js +129 -0
- package/src/js/ui/slot-configuration-window.js +560 -0
- package/src/js/ui/theme-manager.js +61 -0
- package/src/js/ui/toast.js +44 -0
- package/src/js/ui/ui-controller.js +897 -0
- package/src/js/ui/window-switcher.js +275 -0
- package/src/js/utils/basic-autocomplete.js +832 -0
- package/src/js/utils/basic-highlighting.js +473 -0
- package/src/js/utils/basic-tokenizer.js +153 -0
- package/src/js/utils/basic-tokens.js +117 -0
- package/src/js/utils/constants.js +28 -0
- package/src/js/utils/indexeddb-helper.js +225 -0
- package/src/js/utils/merlin-editor-support.js +905 -0
- package/src/js/utils/merlin-highlighting.js +551 -0
- package/src/js/utils/storage.js +125 -0
- package/src/js/utils/string-utils.js +19 -0
- package/src/js/utils/wasm-memory.js +54 -0
- package/src/js/windows/base-window.js +690 -0
- package/src/js/windows/index.js +9 -0
- package/src/js/windows/window-manager.js +375 -0
- package/tests/catch2/catch.hpp +17976 -0
- package/tests/common/basic_program_builder.cpp +119 -0
- package/tests/common/basic_program_builder.hpp +209 -0
- package/tests/common/disk_image_builder.cpp +444 -0
- package/tests/common/disk_image_builder.hpp +141 -0
- package/tests/common/test_helpers.hpp +118 -0
- package/tests/gcr/gcr-test.cpp +142 -0
- package/tests/integration/check-rom.js +70 -0
- package/tests/integration/compare-boot.js +239 -0
- package/tests/integration/crash-trace.js +102 -0
- package/tests/integration/disk-boot-test.js +264 -0
- package/tests/integration/memory-crash.js +108 -0
- package/tests/integration/nibble-read-test.js +249 -0
- package/tests/integration/phase-test.js +159 -0
- package/tests/integration/test_emulator.cpp +291 -0
- package/tests/integration/test_emulator_basic.cpp +91 -0
- package/tests/integration/test_emulator_debug.cpp +344 -0
- package/tests/integration/test_emulator_disk.cpp +153 -0
- package/tests/integration/test_emulator_state.cpp +163 -0
- package/tests/klaus/6502_functional_test.bin +0 -0
- package/tests/klaus/65C02_extended_opcodes_test.bin +0 -0
- package/tests/klaus/klaus_6502_test.cpp +184 -0
- package/tests/klaus/klaus_65c02_test.cpp +197 -0
- package/tests/thunderclock/thunderclock_mmu_test.cpp +304 -0
- package/tests/thunderclock/thunderclock_test.cpp +550 -0
- package/tests/unit/test_assembler.cpp +521 -0
- package/tests/unit/test_audio.cpp +196 -0
- package/tests/unit/test_ay8910.cpp +311 -0
- package/tests/unit/test_basic_detokenizer.cpp +265 -0
- package/tests/unit/test_basic_tokenizer.cpp +382 -0
- package/tests/unit/test_block_device.cpp +259 -0
- package/tests/unit/test_condition_evaluator.cpp +219 -0
- package/tests/unit/test_cpu6502.cpp +1301 -0
- package/tests/unit/test_cpu_addressing.cpp +361 -0
- package/tests/unit/test_cpu_cycle_counts.cpp +409 -0
- package/tests/unit/test_cpu_decimal.cpp +166 -0
- package/tests/unit/test_cpu_interrupts.cpp +285 -0
- package/tests/unit/test_disassembler.cpp +323 -0
- package/tests/unit/test_disk2_card.cpp +330 -0
- package/tests/unit/test_dos33.cpp +273 -0
- package/tests/unit/test_dsk_disk_image.cpp +315 -0
- package/tests/unit/test_expansion_card.cpp +178 -0
- package/tests/unit/test_gcr_encoding.cpp +232 -0
- package/tests/unit/test_keyboard.cpp +262 -0
- package/tests/unit/test_mmu.cpp +555 -0
- package/tests/unit/test_mmu_slots.cpp +323 -0
- package/tests/unit/test_mockingboard.cpp +352 -0
- package/tests/unit/test_mouse_card.cpp +386 -0
- package/tests/unit/test_pascal.cpp +248 -0
- package/tests/unit/test_prodos.cpp +259 -0
- package/tests/unit/test_smartport_card.cpp +321 -0
- package/tests/unit/test_thunderclock.cpp +354 -0
- package/tests/unit/test_via6522.cpp +323 -0
- package/tests/unit/test_video.cpp +319 -0
- package/tests/unit/test_woz_disk_image.cpp +257 -0
- package/vite.config.js +96 -0
- package/wiki/AI-Agent.md +372 -0
- package/wiki/Architecture-Overview.md +303 -0
- package/wiki/Audio-System.md +449 -0
- package/wiki/CPU-Emulation.md +477 -0
- package/wiki/Debugger.md +516 -0
- package/wiki/Disk-Drives.md +161 -0
- package/wiki/Disk-System-Internals.md +547 -0
- package/wiki/Display-Settings.md +88 -0
- package/wiki/Expansion-Slots.md +187 -0
- package/wiki/File-Explorer.md +259 -0
- package/wiki/Getting-Started.md +156 -0
- package/wiki/Home.md +69 -0
- package/wiki/Input-Devices.md +183 -0
- package/wiki/Keyboard-Shortcuts.md +158 -0
- package/wiki/Memory-System.md +364 -0
- package/wiki/Save-States.md +172 -0
- package/wiki/Video-Rendering.md +658 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* basic-tokens.js - Applesoft BASIC token definitions
|
|
3
|
+
*
|
|
4
|
+
* Written by
|
|
5
|
+
* Mike Daley <michael_daley@icloud.com>
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Applesoft BASIC tokens - array index = token value - 0x80
|
|
9
|
+
export const APPLESOFT_TOKENS = [
|
|
10
|
+
"END", // $80
|
|
11
|
+
"FOR", // $81
|
|
12
|
+
"NEXT", // $82
|
|
13
|
+
"DATA", // $83
|
|
14
|
+
"INPUT", // $84
|
|
15
|
+
"DEL", // $85
|
|
16
|
+
"DIM", // $86
|
|
17
|
+
"READ", // $87
|
|
18
|
+
"GR", // $88
|
|
19
|
+
"TEXT", // $89
|
|
20
|
+
"PR#", // $8A
|
|
21
|
+
"IN#", // $8B
|
|
22
|
+
"CALL", // $8C
|
|
23
|
+
"PLOT", // $8D
|
|
24
|
+
"HLIN", // $8E
|
|
25
|
+
"VLIN", // $8F
|
|
26
|
+
"HGR2", // $90
|
|
27
|
+
"HGR", // $91
|
|
28
|
+
"HCOLOR=", // $92
|
|
29
|
+
"HPLOT", // $93
|
|
30
|
+
"DRAW", // $94
|
|
31
|
+
"XDRAW", // $95
|
|
32
|
+
"HTAB", // $96
|
|
33
|
+
"HOME", // $97
|
|
34
|
+
"ROT=", // $98
|
|
35
|
+
"SCALE=", // $99
|
|
36
|
+
"SHLOAD", // $9A
|
|
37
|
+
"TRACE", // $9B
|
|
38
|
+
"NOTRACE", // $9C
|
|
39
|
+
"NORMAL", // $9D
|
|
40
|
+
"INVERSE", // $9E
|
|
41
|
+
"FLASH", // $9F
|
|
42
|
+
"COLOR=", // $A0
|
|
43
|
+
"POP", // $A1
|
|
44
|
+
"VTAB", // $A2
|
|
45
|
+
"HIMEM:", // $A3
|
|
46
|
+
"LOMEM:", // $A4
|
|
47
|
+
"ONERR", // $A5
|
|
48
|
+
"RESUME", // $A6
|
|
49
|
+
"RECALL", // $A7
|
|
50
|
+
"STORE", // $A8
|
|
51
|
+
"SPEED=", // $A9
|
|
52
|
+
"LET", // $AA
|
|
53
|
+
"GOTO", // $AB
|
|
54
|
+
"RUN", // $AC
|
|
55
|
+
"IF", // $AD
|
|
56
|
+
"RESTORE", // $AE
|
|
57
|
+
"&", // $AF - Ampersand (machine language hook)
|
|
58
|
+
"GOSUB", // $B0
|
|
59
|
+
"RETURN", // $B1
|
|
60
|
+
"REM", // $B2
|
|
61
|
+
"STOP", // $B3
|
|
62
|
+
"ON", // $B4
|
|
63
|
+
"WAIT", // $B5
|
|
64
|
+
"LOAD", // $B6
|
|
65
|
+
"SAVE", // $B7
|
|
66
|
+
"DEF", // $B8
|
|
67
|
+
"POKE", // $B9
|
|
68
|
+
"PRINT", // $BA
|
|
69
|
+
"CONT", // $BB
|
|
70
|
+
"LIST", // $BC
|
|
71
|
+
"CLEAR", // $BD
|
|
72
|
+
"GET", // $BE
|
|
73
|
+
"NEW", // $BF
|
|
74
|
+
"TAB(", // $C0
|
|
75
|
+
"TO", // $C1
|
|
76
|
+
"FN", // $C2
|
|
77
|
+
"SPC(", // $C3
|
|
78
|
+
"THEN", // $C4
|
|
79
|
+
"AT", // $C5
|
|
80
|
+
"NOT", // $C6
|
|
81
|
+
"STEP", // $C7
|
|
82
|
+
"+", // $C8
|
|
83
|
+
"-", // $C9
|
|
84
|
+
"*", // $CA
|
|
85
|
+
"/", // $CB
|
|
86
|
+
"^", // $CC
|
|
87
|
+
"AND", // $CD
|
|
88
|
+
"OR", // $CE
|
|
89
|
+
">", // $CF
|
|
90
|
+
"=", // $D0
|
|
91
|
+
"<", // $D1
|
|
92
|
+
"SGN", // $D2
|
|
93
|
+
"INT", // $D3
|
|
94
|
+
"ABS", // $D4
|
|
95
|
+
"USR", // $D5
|
|
96
|
+
"FRE", // $D6
|
|
97
|
+
"SCRN(", // $D7
|
|
98
|
+
"PDL", // $D8
|
|
99
|
+
"POS", // $D9
|
|
100
|
+
"SQR", // $DA
|
|
101
|
+
"RND", // $DB
|
|
102
|
+
"LOG", // $DC
|
|
103
|
+
"EXP", // $DD
|
|
104
|
+
"COS", // $DE
|
|
105
|
+
"SIN", // $DF
|
|
106
|
+
"TAN", // $E0
|
|
107
|
+
"ATN", // $E1
|
|
108
|
+
"PEEK", // $E2
|
|
109
|
+
"LEN", // $E3
|
|
110
|
+
"STR$", // $E4
|
|
111
|
+
"VAL", // $E5
|
|
112
|
+
"ASC", // $E6
|
|
113
|
+
"CHR$", // $E7
|
|
114
|
+
"LEFT$", // $E8
|
|
115
|
+
"RIGHT$", // $E9
|
|
116
|
+
"MID$", // $EA
|
|
117
|
+
];
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* constants.js - Shared constants
|
|
3
|
+
*
|
|
4
|
+
* Written by
|
|
5
|
+
* Mike Daley <michael_daley@icloud.com>
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// localStorage key constants
|
|
9
|
+
export const STORAGE_KEYS = {
|
|
10
|
+
VOLUME: "a2e-volume",
|
|
11
|
+
DISPLAY_SETTINGS: "a2e-display-settings",
|
|
12
|
+
SCREEN_WIDTH: "a2e-screen-width",
|
|
13
|
+
SHOW_DRIVES: "a2e-show-drives",
|
|
14
|
+
DRIVE_SOUNDS: "a2e-drive-sounds",
|
|
15
|
+
CHARSET: "a2e-charset",
|
|
16
|
+
DEBUG_WINDOWS: "a2e-debug-windows",
|
|
17
|
+
ZP_CUSTOM_WATCHES: "zp-custom-watches",
|
|
18
|
+
THEME: "a2e-theme",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Canvas/display dimension constants
|
|
22
|
+
export const DIMENSIONS = {
|
|
23
|
+
MIN_CANVAS_WIDTH: 280,
|
|
24
|
+
MIN_CANVAS_HEIGHT: 200,
|
|
25
|
+
BEZEL_PADDING_X: 88,
|
|
26
|
+
BEZEL_PADDING_Y: 104,
|
|
27
|
+
DEFAULT_PADDING: 32,
|
|
28
|
+
};
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* indexeddb-helper.js - IndexedDB helper utilities
|
|
3
|
+
*
|
|
4
|
+
* Written by
|
|
5
|
+
* Mike Daley <michael_daley@icloud.com>
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Creates a database manager for a specific IndexedDB database.
|
|
10
|
+
* Handles connection caching, database opening, and provides
|
|
11
|
+
* Promise-based wrappers for common operations.
|
|
12
|
+
*
|
|
13
|
+
* @param {Object} config - Database configuration
|
|
14
|
+
* @param {string} config.dbName - The database name
|
|
15
|
+
* @param {number} config.version - The database version
|
|
16
|
+
* @param {function} config.onUpgrade - Upgrade handler (event) => void
|
|
17
|
+
* @returns {Object} Database manager with helper methods
|
|
18
|
+
*/
|
|
19
|
+
export function createDatabaseManager({ dbName, version, onUpgrade }) {
|
|
20
|
+
let cachedDb = null;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Open the database, using cached connection if available
|
|
24
|
+
* @returns {Promise<IDBDatabase>}
|
|
25
|
+
*/
|
|
26
|
+
async function open() {
|
|
27
|
+
if (cachedDb) {
|
|
28
|
+
return cachedDb;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
const request = indexedDB.open(dbName, version);
|
|
33
|
+
|
|
34
|
+
request.onerror = () => {
|
|
35
|
+
console.error(`Failed to open database ${dbName}:`, request.error);
|
|
36
|
+
reject(request.error);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
request.onsuccess = () => {
|
|
40
|
+
cachedDb = request.result;
|
|
41
|
+
resolve(cachedDb);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
request.onupgradeneeded = onUpgrade;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Get a record by key from a store
|
|
50
|
+
* @param {string} storeName - The object store name
|
|
51
|
+
* @param {*} key - The key to look up
|
|
52
|
+
* @returns {Promise<*>} The record or undefined
|
|
53
|
+
*/
|
|
54
|
+
async function get(storeName, key) {
|
|
55
|
+
const db = await open();
|
|
56
|
+
const transaction = db.transaction(storeName, "readonly");
|
|
57
|
+
const store = transaction.objectStore(storeName);
|
|
58
|
+
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
const request = store.get(key);
|
|
61
|
+
request.onsuccess = () => resolve(request.result);
|
|
62
|
+
request.onerror = () => reject(request.error);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Put (insert or update) a record in a store
|
|
68
|
+
* @param {string} storeName - The object store name
|
|
69
|
+
* @param {*} record - The record to store
|
|
70
|
+
* @returns {Promise<*>} The key of the stored record
|
|
71
|
+
*/
|
|
72
|
+
async function put(storeName, record) {
|
|
73
|
+
const db = await open();
|
|
74
|
+
const transaction = db.transaction(storeName, "readwrite");
|
|
75
|
+
const store = transaction.objectStore(storeName);
|
|
76
|
+
|
|
77
|
+
return new Promise((resolve, reject) => {
|
|
78
|
+
const request = store.put(record);
|
|
79
|
+
request.onsuccess = () => resolve(request.result);
|
|
80
|
+
request.onerror = () => reject(request.error);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Add a new record to a store (fails if key exists)
|
|
86
|
+
* @param {string} storeName - The object store name
|
|
87
|
+
* @param {*} record - The record to add
|
|
88
|
+
* @returns {Promise<*>} The key of the added record
|
|
89
|
+
*/
|
|
90
|
+
async function add(storeName, record) {
|
|
91
|
+
const db = await open();
|
|
92
|
+
const transaction = db.transaction(storeName, "readwrite");
|
|
93
|
+
const store = transaction.objectStore(storeName);
|
|
94
|
+
|
|
95
|
+
return new Promise((resolve, reject) => {
|
|
96
|
+
const request = store.add(record);
|
|
97
|
+
request.onsuccess = () => resolve(request.result);
|
|
98
|
+
request.onerror = () => reject(request.error);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Delete a record by key from a store
|
|
104
|
+
* @param {string} storeName - The object store name
|
|
105
|
+
* @param {*} key - The key to delete
|
|
106
|
+
* @returns {Promise<void>}
|
|
107
|
+
*/
|
|
108
|
+
async function remove(storeName, key) {
|
|
109
|
+
const db = await open();
|
|
110
|
+
const transaction = db.transaction(storeName, "readwrite");
|
|
111
|
+
const store = transaction.objectStore(storeName);
|
|
112
|
+
|
|
113
|
+
return new Promise((resolve, reject) => {
|
|
114
|
+
const request = store.delete(key);
|
|
115
|
+
request.onsuccess = () => resolve();
|
|
116
|
+
request.onerror = () => reject(request.error);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Count records in a store
|
|
122
|
+
* @param {string} storeName - The object store name
|
|
123
|
+
* @returns {Promise<number>}
|
|
124
|
+
*/
|
|
125
|
+
async function count(storeName) {
|
|
126
|
+
const db = await open();
|
|
127
|
+
const transaction = db.transaction(storeName, "readonly");
|
|
128
|
+
const store = transaction.objectStore(storeName);
|
|
129
|
+
|
|
130
|
+
return new Promise((resolve, reject) => {
|
|
131
|
+
const request = store.count();
|
|
132
|
+
request.onsuccess = () => resolve(request.result);
|
|
133
|
+
request.onerror = () => reject(request.error);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Iterate over records using a cursor
|
|
139
|
+
* @param {string} storeName - The object store name
|
|
140
|
+
* @param {Object} options - Cursor options
|
|
141
|
+
* @param {string} [options.indexName] - Optional index name to use
|
|
142
|
+
* @param {IDBKeyRange} [options.range] - Optional key range
|
|
143
|
+
* @param {string} [options.direction] - Cursor direction ('next', 'prev', etc.)
|
|
144
|
+
* @param {function} callback - Called for each record: (value, cursor) => void
|
|
145
|
+
* Return false to stop iteration
|
|
146
|
+
* @returns {Promise<void>}
|
|
147
|
+
*/
|
|
148
|
+
async function iterate(storeName, options, callback) {
|
|
149
|
+
const db = await open();
|
|
150
|
+
const transaction = db.transaction(storeName, "readonly");
|
|
151
|
+
const store = transaction.objectStore(storeName);
|
|
152
|
+
const source = options.indexName ? store.index(options.indexName) : store;
|
|
153
|
+
|
|
154
|
+
return new Promise((resolve, reject) => {
|
|
155
|
+
const request = source.openCursor(options.range || null, options.direction || "next");
|
|
156
|
+
|
|
157
|
+
request.onsuccess = (event) => {
|
|
158
|
+
const cursor = event.target.result;
|
|
159
|
+
if (cursor) {
|
|
160
|
+
const result = callback(cursor.value, cursor);
|
|
161
|
+
if (result !== false) {
|
|
162
|
+
cursor.continue();
|
|
163
|
+
} else {
|
|
164
|
+
resolve();
|
|
165
|
+
}
|
|
166
|
+
} else {
|
|
167
|
+
resolve();
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
request.onerror = () => reject(request.error);
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Iterate over records with write access (for deletion during iteration)
|
|
176
|
+
* @param {string} storeName - The object store name
|
|
177
|
+
* @param {Object} options - Cursor options
|
|
178
|
+
* @param {string} [options.indexName] - Optional index name to use
|
|
179
|
+
* @param {IDBKeyRange} [options.range] - Optional key range
|
|
180
|
+
* @param {string} [options.direction] - Cursor direction ('next', 'prev', etc.)
|
|
181
|
+
* @param {function} callback - Called for each record: (value, cursor, store) => void
|
|
182
|
+
* @returns {Promise<void>}
|
|
183
|
+
*/
|
|
184
|
+
async function iterateWithWrite(storeName, options, callback) {
|
|
185
|
+
const db = await open();
|
|
186
|
+
const transaction = db.transaction(storeName, "readwrite");
|
|
187
|
+
const store = transaction.objectStore(storeName);
|
|
188
|
+
const source = options.indexName ? store.index(options.indexName) : store;
|
|
189
|
+
|
|
190
|
+
return new Promise((resolve, reject) => {
|
|
191
|
+
const request = source.openCursor(options.range || null, options.direction || "next");
|
|
192
|
+
|
|
193
|
+
request.onsuccess = (event) => {
|
|
194
|
+
const cursor = event.target.result;
|
|
195
|
+
if (cursor) {
|
|
196
|
+
callback(cursor.value, cursor, store);
|
|
197
|
+
cursor.continue();
|
|
198
|
+
} else {
|
|
199
|
+
resolve();
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
request.onerror = () => reject(request.error);
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Get raw database reference for complex operations
|
|
208
|
+
* @returns {Promise<IDBDatabase>}
|
|
209
|
+
*/
|
|
210
|
+
async function getDatabase() {
|
|
211
|
+
return open();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
open,
|
|
216
|
+
get,
|
|
217
|
+
put,
|
|
218
|
+
add,
|
|
219
|
+
remove,
|
|
220
|
+
count,
|
|
221
|
+
iterate,
|
|
222
|
+
iterateWithWrite,
|
|
223
|
+
getDatabase,
|
|
224
|
+
};
|
|
225
|
+
}
|