git.iko.soy/purescript-indexed-DB
on master help sign in
.eslintignore+1−1
since splitsnapshots
unifiedsplit
@@ -1 +1 @@
1−/dist/*
1+dist/*
.github/browserstack.pngnew file

Binary file not shown.

.gitignore+3
@@ -71,8 +71,11 @@ typings/
71# Generated files
71# Generated files
72output
72output
73generated-docs
73generated-docs
74+releases
74.pulp-cache
75.pulp-cache
75.psc*
76.psc*
76.purs*
77.purs*
77.psa*
78.psa*
78dist
79dist
80+.spago
81+test.js
.travis.ymlnew file+37
@@ -0,0 +1,37 @@
1+language: node_js
2+node_js:
3+ - node
4+
5+addons:
6+ browserstack:
7+ username: $BROWSERSTACK_USERNAME
8+ access_key: $BROWSERSTACK_ACCESSKEY
9+
10+before_install:
11+ - export PATH=./node_modules/.bin:$PATH
12+
13+install:
14+ - npm install
15+ - bower install
16+
17+script:
18+ - npm run test:browserstack
19+
20+after_script:
21+ - npm run prepare:release
22+
23+deploy:
24+ - provider: releases
25+ api_key: $GITHUB_TOKEN
26+ skip_cleanup: true
27+ file_glob: true
28+ file: releases/github/*
29+ on:
30+ tags: true
31+ branch: master
32+
33+ - provider: script
34+ script: deploy.sh
35+ on:
36+ tags: true
37+ branch: master
README.md+75−251
@@ -1,283 +1,107 @@
1−## API
1+PureScript IndexedDB [![](https://img.shields.io/badge/doc-pursuit-60b5cc.svg)](http://pursuit.purescript.org/packages/purescript-indexeddb) [![Build Status](https://travis-ci.org/truqu/purescript-indexedDB.svg?branch=master)](https://travis-ci.org/truqu/purescript-indexedDB)
2+=====
2
3
3−This document is a general grooming of the current state of the IndexedDB API. It covers only
4+This package offers complete bindings and type-safety upon the [IndexedDB API](https://w3c.github.io/IndexedDB).
4−the features specified in the official specs.
5
5
6−### Remarks
6+## Overview
7
7
8−- Better define a 'all' range instead of using Maybe `IDBKeyRange` in function
8+The `IDBCore` and `IDBFactory` are the two entry points required to create and connect to an
9−signatures (Nothing meaning 'all').
9+indexed database. From there, modules are divided such that each of them covers a specific IDB
10+interface.
10
11
11−- Some errors (like TypeError or DataError) can be avoided via static typing
12+They are designed to be used as qualified imports such that each method gets prefixed with a
13+menaingful namespace (e.g `IDBIndex.get`, `IDBObjectStore.openCursor` ...)
12
14
13−- The `IDBRequest` is mostly a callback result holding an error or a value. We probably want to
15+Here's a quick example of what it look likes.
14− use a typed Except or ExceptT instead.
16+```purescript
17+module Main where
15
18
19+import Prelude
16
20
17−### Table of Contents
21+import Control.Monad.Aff (Aff, launchAff_)
22+import Control.Monad.Aff.Console (CONSOLE, log)
23+import Control.Monad.Eff.Exception (EXCEPTION)
24+import Control.Monad.Eff (Eff)
25+import Data.Maybe (Maybe(..), maybe)
18
26
19−- [IDBCursor](#idbcursor)
27+import Database.IndexedDB.Core
20−- [IDBCursorWithValue](#idbcursorwithvalue)
28+import Database.IndexedDB.IDBFactory as IDBFactory
21−- [IDBCursorDirection](#idbcursordirection)
29+import Database.IndexedDB.IDBDatabase as IDBDatabase
22−- [IDBDatabase](#idbdatabase)
30+import Database.IndexedDB.IDBObjectStore as IDBObjectStore
23−- [IDBError](#idberror)
31+import Database.IndexedDB.IDBIndex as IDBIndex
24−- [IDBFactory](#idbfactory)
32+import Database.IndexedDB.IDBTransaction as IDBTransaction
25−- [IDBIndex](#idbindex)
33+import Database.IndexedDB.IDBKeyRange as IDBKeyRange
26−- [IDBKeyRange](#idbkeyrange)
27−- [IDBRequest](#idbrequest)
28−- [IDBOpenDBRequest](#idbopendbrequest)
29−- [IDBObjectStore](#idbobjectstore)
30−- [IDBTransaction](#idbtransaction)
31−- [IDBTransactionMode](#idbtransactionmode)
32
34
33
35
34−---
36+main :: Eff (idb :: IDB, exception :: EXCEPTION, console :: CONSOLE) Unit
37+main = launchAff_ do
38+ db <- IDBFactory.open "db" Nothing { onBlocked : Nothing
39+ , onUpgradeNeeded : Just onUpgradeNeeded
40+ }
35
41
42+ tx <- IDBDatabase.transaction db ["store"] ReadOnly
43+ store <- IDBTransaction.objectStore tx "store"
44+ (val :: Maybe String) <- IDBObjectStore.get store (IDBKeyRange.only 1)
45+ log $ maybe "not found" id val
36
46
37−#### IDBCursor
38−> https://developer.mozilla.org/en-US/docs/Web/API/IDBCursor
39
47
40−- source :: [IDBIndex](#idbindex), [IDBObjectStore](#idbobjectstore)
48+onUpgradeNeeded :: forall e. Database -> Transaction -> { oldVersion :: Int } -> Eff (idb :: IDB, exception :: EXCEPTION | e) Unit
41−- direction :: [IDBCursorDirection](#idbcursordirection)
49+onUpgradeNeeded db _ _ = launchAff_ do
42−- key :: [IDBKey](#idbkey)
50+ store <- IDBDatabase.createObjectStore db "store" IDBDatabase.defaultParameters
43−- primaryKey :: [IDBKey](#idbkey)
51+ _ <- IDBObjectStore.add store "patate" (Just 1)
44−- value :: Any
52+ _ <- IDBObjectStore.add store { property: 42 } (Just 2)
45−- advance :: [IDBCursor](#idbcursor) -> Int -> ()
53+ _ <- IDBObjectStore.createIndex store "index" ["property"] IDBObjectStore.defaultParameters
46− > `throw` TransactionInactiveError, TypeError, InvalidStateError
54+ pure unit
47−- continue :: [IDBCursor](#idbcursor) -> Maybe [IDBKey](#idbkey) -> ()
55+```
48− > `throw` TransactionInactivError, DataError, InvalidStateError
49−- continuePrimaryKey :: [IDBCursor](#idbcursor) -> [IDBKey](#idbkey) -> [IDBKey](#idbkey) -> ()
50− > `throw` TransactionInactiveError, DataError, InvalidStateError, InvalidAccessError
51
56
57+## Notes
52
58
53−---
59+### Errors
60+Errors normally thrown by the IDB\* interfaces are wrapped in the `Aff` Monad as `Error` where
61+the `name` corresponds to the error's name (e.g. "InvalidStateError"). Pattern matching can
62+therefore be done on any error to handle specific errors thrown by the API.
54
63
64+### Examples
65+The `test` folder contains a great amount of examples showing practical usage of the IDB\*
66+interfaces. Do not hesitate to have a peek should you wonder how to use one of the module. The
67+wrapper tries to keep as much as possible an API consistent with the original IndexedDB API.
68+Hence, it should be quite straightforward to translate any JavaScript example to a PureScript
69+one.
55
70
56−#### IDBCursorWithValue
71+## Changelog
57−> https://developer.mozilla.org/en-US/docs/Web/API/IDBCursorwithValue
58
72
59−Inherit from [IDBCursor](#idbcursor)
73+#### v3.0.0
60
74
61−- delete :: [IDBCursor](#idbcursor) -> [IDBRequest](#idbrequest) ()
75+- callback to `onUpgradeNeeded` event now provide a record with the database old version.
62− > `throw` TransactionInactiveError, ReadOnlyError, InvalidStateError
63−- update :: [IDBCursor](#idbcursor) -> a -> [IDBRequest](#idbrequest) a
64− > `throw` TransactionInactiveError, ReadOnlyError, InvalidStateError, DataError, DataCloneError
65
76
77+#### v2.0.0
66
78
67−---
79+- review interface implementation (use of opaque classes to improve readability without compromising
80+ the reusability). The API doesn't really change from a user perspective though.
68
81
82+- make the Key more opaque (by having an IDBKey instance for Foreign types)
69
83
70−#### IDBCursorDirection
84+- Upgrade purescript-exceptions to 3.1.0 and leverage the new `name` accessor
71−> https://www.w3.org/TR/IndexedDB/#idl-def-IDBCursorDirection
72
85
73−data [IDBCursorDirection](#idbcursordirection)
74− = Next
75− | NextUnique
76− | Prev
77− | PrevUnique
78
86
87+#### v1.0.0
79
88
80−---
89+- [Indexed Database API 2.0](https://w3c.github.io/IndexedDB/) totally covered apart from
90+ - `index.getAll` method (and the associated one for the IDBObjectStore)
91+ - binary keys
81
92
93+## Documentation
82
94
83−#### IDBDatabase
95+Module documentation is [published on Pursuit](http://pursuit.purescript.org/packages/purescript-indexeddb).
84−> https://www.w3.org/TR/IndexedDB/#idl-def-IDBDatabase
85
96
86−- name :: String
97+## Testing
87−- version :: Long
98+Tested in the cloud on multiple browsers and operating systems thanks to [BrowserStack](https://www.browserstack.com)
88−- objectStoreNames :: [String]
89−- close :: [IDBDatabase](#idbdatabase) -> ()
90−- createObjectStore :: [IDBDatabase](#idbdatabase) -> String -> { keyPath :: String, autoIncrement :: Bool } -> [IDBObjectStore](#idbobjectstore)
91− > `throw` InvalidStateError, TransactionInactiveError, ConstraintError, InvalidAccessError
92−- deleteObjectStore :: [IDBDatabase](#idbdatabase) -> String -> ()
93− > `throw` InvalidSateError, TransactionInactiveError, NotFoundError
94−- transaction :: [IDBDatabase](#idbdatabase) -> [String] -> [IDBTransactionMode](#idbtransactionmode) -> [IDBTransaction](#idbtransaction)
95− > `throw` InvalidStateError, NotFoundError, TypeError, InvalidAccessError
96
99
97−##### Events
98−- onabort
99−- onerror
100−- onversionchange
101
100
101+| IE / Edge | Chrome | Firefox | Safari | Opera | Android | iOS Safari |
102+| ----------| ------ | ------- | ------- | ----- | ------- | ---------- |
103+| - | >= 57 | >= 51 | - | >= 46 | - | - |
102
104
103−---
105+<p align="center">
104−
106+ <a href="https://www.browserstack.com"><img alt="browserstack" src=".github/browserstack.png" /></a>
105−
107+</p>
106−#### IDBError
107−> https://www.w3.org/TR/IndexedDB/#exceptions
108−
109−data IDBError
110− = AbortError
111− | ConstraintError
112− | QuotaExceededError
113− | UnknownError
114− | NoError
115− | VersionError
116−
117−
118−---
119−
120−
121−#### IDBFactory
122−> https://www.w3.org/TR/IndexedDB/#idl-def-IDBFactory
123−
124−The method `cmp` isn't included; instead, we consider [IDBKey](#idbkey) to be comparable.
125−
126−- open :: String -> Maybe Long -> [IDBOpenDBRequest](#idbopendbrequest) ()
127−- deleteDatabase :: String -> [IDBOpenDBRequest](#idbopendbrequest) ()
128−
129−
130−---
131−
132−
133−#### IDBIndex
134−> https://www.w3.org/TR/IndexedDB/#index-concept
135−> https://www.w3.org/TR/IndexedDB/#index
136−
137−- name :: String
138−- objectStore :: [IDBObjectStore](#idbobjectstore)
139−- keyPath :: String
140−- multiEntry :: Bool
141−- unique :: Bool
142−- count :: [IDBIndex](#idbindex) -> Maybe [IDBKeyRange](#idbkeyrange) -> [IDBRequest](#idbrequest) Int
143− > `throw` TransactionInactiveError, DataError, InvalidStateError
144−- get :: [IDBIndex](#idbindex) -> [IDBKeyRange](#idbkeyrange) -> [IDBRequest](#idbrequest) a
145− > `throw` TransactionInactiveError, DataError, InvalidStateError
146−- getKey :: [IDBIndex](#idbindex) -> [IDBKeyRange](#idbkeyrange) -> [IDBRequest](#idbrequest) [IDBKey](#idbkey)
147− > `throw` TransactionInactiveError, DataError, InvalidStateError
148−- openCursor :: [IDBIndex](#idbindex) -> Maybe [IDBKeyRange](#idbkeyrange) -> Maybe Direction -> [IDBRequest](#idbrequest) CursorWithValue
149− > `throw` TransactionInactiveError, DataError, TypeError, InvalidStateError
150−- openKeyCursor :: [IDBIndex](#idbindex) -> Maybe [IDBKeyRange](#idbkeyrange)-> Maybe Direction -> [IDBRequest](#idbrequest) Cursor
151− > `throw` TransactionInactiveError, DataError, TypeError, InvalidStateError
152−
153−
154−---
155−
156−
157−#### IDBKey
158−> https://www.w3.org/TR/IndexedDB/#key-construct
159−
160−Should be comparable / derive Ord
161−
162−data IDBKey
163− = Int Int
164− | Float Float
165− | String String
166− | Date Date
167− | Array [[IDBKey](#idbkey)]
168−
169−
170−---
171−
172−
173−#### IDBKeyRange
174−> https://www.w3.org/TR/IndexedDB/#idl-def-IDBKeyRange
175−
176−- lower :: [IDBKey](#idbkey)
177−- upper :: [IDBKey](#idbkey)
178−- lowerOpen :: Boolean
179−- upperOpen :: Boolean
180−- bound :: [IDBKey](#idbkey) -> [IDBKey](#idbkey) -> Bool -> Bool -> [IDBKeyRange](#idbkeyrange)
181− > `throw` DataError
182−- only :: [IDBKey](#idbkey) -> [IDBKeyRange](#idbkeyrange)
183− > `throw` DataError
184−- lowerBound :: [IDBKey](#idbkey) -> Bool -> [IDBKeyRange](#idbkeyrange)
185− > `throw` DataError
186−- upperBound :: [IDBKey](#idbkey) -> Bool -> [IDBKeyRange](#idbkeyrange)
187− > `throw` DataError
188−- includes :: [IDBKey](#idbkey) -> Boolean
189− > `throw` DataError
190−
191−
192−---
193−
194−
195−#### IDBRequest
196−> https://www.w3.org/TR/IndexedDB/#idl-def-IDBRequest
197−
198−- error :: [IDBError](#idberror)
199−- result :: a
200−- source :: [IDBIndex](#idbindex) | [IDBObjectStore](#idbobjectstore) | [IDBCursor](#idbcursor)
201−- readyState :: Bool
202−- transaction :: Maybe [IDBTransaction](#idbtransaction)
203−
204−##### Events
205−- onerror
206−- onsuccess
207−
208−
209−---
210−
211−
212−#### IDBOpenDBRequest
213−> https://www.w3.org/TR/IndexedDB/#idl-def-IDBRequest
214−
215−Inherit from [IDBRequest](#idbrequest)
216−
217−##### Events
218− - onblocked
219− - onupgradeneeded
220−
221−
222−---
223−
224−
225−#### IDBObjectStore
226−> https://www.w3.org/TR/IndexedDB/#idl-def-IDBObjectStore
227−
228−- indexNames:: [String]
229−- keyPath :: String
230−- name :: String
231−- transaction :: [IDBTransaction](#idbtransaction)
232−- autoIncrement :: Bool
233−- add :: [IDBObjectStore](#idbobjectstore) -> Value -> Maybe [IDBKey](#idbkey) -> [IDBRequest](#idbrequest) ()
234− > `throw` ReadOnlyError, TransactionInactiveError, DataError, InvalidStateError, DataCloneError
235−- clear :: [IDBObjectStore](#idbobjectstore) -> [IDBRequest](#idbrequest) ()
236− > `throw` ReadOnlyError, TransactionInactiveError
237−- count :: [IDBObjectStore](#idbobjectstore) -> [IDBKeyRange](#idbkeyrange) -> [IDBRequest](#idbrequest) Int
238− > `throw` InvalidStateError, TransactionInactiveError, DataError
239−- createIndex :: [IDBObjectStore](#idbobjectstore) -> String -> String -> { unique :: Bool, multiEntry :: true } -> [IDBIndex](#idbindex)
240− > `throw` ConstraintError, InvalidAccessError, InvalidStateError, SyntaxError, TransactionInactiveError
241−- delete :: [IDBObjectStore](#idbobjectstore) -> [IDBKeyRange](#idbkeyrange) -> [IDBRequest](#idbrequest) ()
242− > `throw` TransactionInactiveError, ReadOnlyError, InvalidStateError, DataError
243−- deleteIndex :: [IDBObjectStore](#idbobjectstore) -> String -> ()
244− > `throw` InvalidStateError, TransactionInactiveError, NotFoundError
245−- get :: [IDBObjectStore](#idbobjectstore) -> [IDBKeyRange](#idbkeyrange) -> [IDBRequest](#idbrequest) a
246− > `throw` InvalidStateError, TransactionInactiveError, DataError
247−- index :: [IDBObjectStore](#idbobjectstore) -> String -> [IDBIndex](#idbindex)
248− > `throw` InvalidStateError, NotfoundError
249−- openCursor :: [IDBObjectStore](#idbobjectstore) -> Maybe [IDBKeyRange](#idbkeyrange) -> Maybe [IDBCursorDirection](#idbcursordirection) -> [IDBRequest](#idbrequest) [IDBCursorWithValue](#idbcursorwithvalue)
250− > `throw` TransactionInactiveError, InvalidStateError, DataError
251−- put :: [IDBObjectStore](#idbobjectstore) -> a -> [IDBKey](#idbkey) -> [IDBRequest](#idbrequest) ()
252− > `throw` ReadOnlyError, TransactionInactiveError, DataError, InvalidStateError, DataCloneError
253−
254−
255−---
256−
257−
258−#### IDBTransaction
259−> https://www.w3.org/TR/IndexedDB/#idl-def-IDBTransaction
260−
261−- db :: [IDBDatabase](#idbdatabase)
262−- error :: [IDBError](#idberror)
263−- mode :: [IDBTransactionMode](#idbtransactionmode)
264−- abort :: [IDBTransaction](#idbtransaction) -> ()
265− > `throw` InvalidStateError
266−- objectStore :: [IDBTransaction](#idbtransaction) -> String -> [IDBObjectStore](#idbobjectstore)
267− > `throw` InvalidStateError, NotFoundError
268−
269−##### Events
270−- onabort
271−- oncomplete
272−- onerror
273−
274−---
275−
276−
277−#### IDBTransactionMode
278−> https://www.w3.org/TR/IndexedDB/#idl-def-IDBTransactionMode
279−
280−data IDBTransactionMode
281− = ReadOnly
282− | ReadWrite
283− | VersionChange
bower.jsondeleted−19
@@ -1,19 +0,0 @@
1−{
2− "name": "purescript-indexeddb",
3− "ignore": [
4− "**/.*",
5− "node_modules",
6− "bower_components",
7− "output"
8− ],
9− "dependencies": {
10− "purescript-maybe": "3.0.0",
11− "purescript-aff": "3.1.0",
12− "purescript-test-unit": "11.0.0",
13− "purescript-spec": "1.0.0",
14− "purescript-datetime": "3.2.0"
15− },
16− "devDependencies": {
17− "purescript-psci-support": "3.0.0"
18− }
19−}
index.jsnew file+4
@@ -0,0 +1,4 @@
1+import "fake-indexeddb/auto";
2+import * as Main from './output/Test.Main/index.js';
3+
4+Main.main();
karma.conf.jsdeleted−25
@@ -1,25 +0,0 @@
1−module.exports = config => {
2− config.set({
3− autoWatch: true,
4− browsers: ["Chrome"],
5− files: [
6− "dist/karma/index.js",
7− ],
8− frameworks: [
9− "mocha",
10− ],
11− plugins: [
12− "karma-chrome-launcher",
13− "karma-firefox-launcher",
14− "karma-spec-reporter",
15− "karma-mocha",
16− ],
17− reporters: ["spec"],
18− singleRun: false,
19− client: {
20− mocha: {
21− timeout: 10000
22− }
23− }
24− });
25−};
package.json+8−39
@@ -1,46 +1,15 @@
1{
1{
2− "name": "purescript-indexeddb",
2+ "name": "purescript-indexed-DB",
3 "version": "1.0.0",
3 "version": "1.0.0",
4− "description": "This document is a general grooming of the current state of the IndexedDB API. It covers only the features specified in the official specs.",
5− "main": "bundle.js",
4+ "main": "index.js",
5+ "repository": "https://github.com/ilyakooo0/purescript-indexed-DB.git",
6+ "author": "iko <mail@iko.soy>",
7+ "license": "MIT",
6 "scripts": {
8 "scripts": {
7− "test:build": "pulp browserify -j 4 --main \"Test.Main\" -I test --to dist/karma/index.js",
9+ "test": "spago build && node index.js"
8− "test:run": "karma start --single-run",
9− "test:watch": "find . -type f -regex '.*\\(src\\|test\\).*' ! -regex '.*bower_components.*\\|.*node_modules.*|.*\\.sw.*' | entr -s 'npm run test'",
10− "test": "npm run test:build && npm run test:run",
11− "build": "pulp browserify -j 4 -O --to dist/bundle.js",
12− "start": "find . -type f -regex '.*\\(src\\|test\\).*' ! -regex '.*bower_components.*\\|.*node_modules.*|.*\\.sw.*' | entr -s 'clear; pulp browserify -j 4 -t dist/bundle.js'",
13− "start:test": "karma start"
14 },
10 },
15 "dependencies": {
11 "dependencies": {
16− "eslint": "^3.19.0",
12+ "fake-indexeddb": "^4.0.1"
17− "eslint-config-airbnb": "^15.0.1",
18− "eslint-plugin-import": "^2.3.0",
19− "eslint-plugin-jsx-a11y": "^5.0.3",
20− "eslint-plugin-react": "^7.1.0",
21− "karma": "^1.7.0"
22 },
13 },
23− "devDependencies": {
24− "eslint": "3.19.0",
25− "eslint-config-airbnb": "15.0.1",
26− "eslint-plugin-import": "2.5.0",
27− "eslint-plugin-jsx-a11y": "5.0.3",
28− "eslint-plugin-react": "7.1.0",
29− "karma": "1.7.0",
30− "karma-chrome-launcher": "2.1.1",
31− "karma-firefox-launcher": "1.0.1",
32− "karma-mocha": "^1.3.0",
33− "karma-spec-reporter": "0.0.31",
34− "mocha": "^3.4.2"
35− },
36− "repository": {
37− "type": "git",
14+ "type": "module"
38− "url": "git+https://github.com/Truqu/purescript-indexeddb.git"
39− },
40− "author": "",
41− "license": "ISC",
42− "bugs": {
43− "url": "https://github.com/Truqu/purescript-indexeddb/issues"
44− },
45− "homepage": "https://github.com/Truqu/purescript-indexeddb#readme"
46}
15}
packages.dhallnew file+105
@@ -0,0 +1,105 @@
1+{-
2+Welcome to your new Dhall package-set!
3+
4+Below are instructions for how to edit this file for most use
5+cases, so that you don't need to know Dhall to use it.
6+
7+## Use Cases
8+
9+Most will want to do one or both of these options:
10+1. Override/Patch a package's dependency
11+2. Add a package not already in the default package set
12+
13+This file will continue to work whether you use one or both options.
14+Instructions for each option are explained below.
15+
16+### Overriding/Patching a package
17+
18+Purpose:
19+- Change a package's dependency to a newer/older release than the
20+ default package set's release
21+- Use your own modified version of some dependency that may
22+ include new API, changed API, removed API by
23+ using your custom git repo of the library rather than
24+ the package set's repo
25+
26+Syntax:
27+where `entityName` is one of the following:
28+- dependencies
29+- repo
30+- version
31+-------------------------------
32+let upstream = --
33+in upstream
34+ with packageName.entityName = "new value"
35+-------------------------------
36+
37+Example:
38+-------------------------------
39+let upstream = --
40+in upstream
41+ with halogen.version = "master"
42+ with halogen.repo = "https://example.com/path/to/git/repo.git"
43+
44+ with halogen-vdom.version = "v4.0.0"
45+ with halogen-vdom.dependencies = [ "extra-dependency" ] # halogen-vdom.dependencies
46+-------------------------------
47+
48+### Additions
49+
50+Purpose:
51+- Add packages that aren't already included in the default package set
52+
53+Syntax:
54+where `<version>` is:
55+- a tag (i.e. "v4.0.0")
56+- a branch (i.e. "master")
57+- commit hash (i.e. "701f3e44aafb1a6459281714858fadf2c4c2a977")
58+-------------------------------
59+let upstream = --
60+in upstream
61+ with new-package-name =
62+ { dependencies =
63+ [ "dependency1"
64+ , "dependency2"
65+ ]
66+ , repo =
67+ "https://example.com/path/to/git/repo.git"
68+ , version =
69+ "<version>"
70+ }
71+-------------------------------
72+
73+Example:
74+-------------------------------
75+let upstream = --
76+in upstream
77+ with benchotron =
78+ { dependencies =
79+ [ "arrays"
80+ , "exists"
81+ , "profunctor"
82+ , "strings"
83+ , "quickcheck"
84+ , "lcg"
85+ , "transformers"
86+ , "foldable-traversable"
87+ , "exceptions"
88+ , "node-fs"
89+ , "node-buffer"
90+ , "node-readline"
91+ , "datetime"
92+ , "now"
93+ ]
94+ , repo =
95+ "https://github.com/hdgarrood/purescript-benchotron.git"
96+ , version =
97+ "v7.0.0"
98+ }
99+-------------------------------
100+-}
101+
102+let upstream =
103+ https://github.com/purescript/package-sets/releases/download/psc-0.15.4-20221124/packages.dhall
104+ sha256:792255bbd8e2141468d967325f3e78246cb7e500bf3ab6d76d5b22dffbb09d49
105+in upstream
spago.dhallnew file+42
@@ -0,0 +1,42 @@
1+{-
2+Welcome to a Spago project!
3+You can edit this file as you like.
4+
5+Need help? See the following resources:
6+- Spago documentation: https://github.com/purescript/spago
7+- Dhall language tour: https://docs.dhall-lang.org/tutorials/Language-Tour.html
8+
9+When creating a new Spago project, you can use
10+`spago init --no-comments` or `spago init -C`
11+to generate this file without the comments in this block.
12+-}
13+{ name = "indexed-db"
14+, dependencies =
15+ [ "aff"
16+ , "arrays"
17+ , "avar"
18+ , "console"
19+ , "control"
20+ , "datetime"
21+ , "effect"
22+ , "either"
23+ , "enums"
24+ , "exceptions"
25+ , "foldable-traversable"
26+ , "foreign"
27+ , "functions"
28+ , "identity"
29+ , "lists"
30+ , "maybe"
31+ , "nonempty"
32+ , "now"
33+ , "nullable"
34+ , "prelude"
35+ , "read"
36+ , "spec"
37+ , "transformers"
38+ , "tuples"
39+ ]
40+, packages = ./packages.dhall
41+, sources = [ "src/**/*.purs", "test/**/*.purs" ]
42+}
src/Database/IndexedDB/Core.js+6−6
@@ -3,7 +3,7 @@ const toArray = function toArray(xs) {
3};
3};
4
4
5
5
6−exports._showCursor = function _showCursor(cursor) {
6+export function _showCursor(cursor) {
7 return '(IDBCursor ' +
7 return '(IDBCursor ' +
8 '{ direction: ' + cursor.direction +
8 '{ direction: ' + cursor.direction +
9 ', key: ' + cursor.key +
9 ', key: ' + cursor.key +
@@ -11,7 +11,7 @@ exports._showCursor = function _showCursor(cursor) {
11 ' })';
11 ' })';
12};
12};
13
13
14−exports._showDatabase = function _showDatabase(db) {
14+export function _showDatabase(db) {
15 return '(IDBDatabase ' +
15 return '(IDBDatabase ' +
16 '{ name: ' + db.name +
16 '{ name: ' + db.name +
17 ', objectStoreNames: [' + toArray(db.objectStoreNames).join(', ') + ']' +
17 ', objectStoreNames: [' + toArray(db.objectStoreNames).join(', ') + ']' +
@@ -19,7 +19,7 @@ exports._showDatabase = function _showDatabase(db) {
19 ' })';
19 ' })';
20};
20};
21
21
22−exports._showIndex = function _showIndex(index) {
22+export function _showIndex(index) {
23 return '(IDBIndex ' +
23 return '(IDBIndex ' +
24 '{ name: ' + index.name +
24 '{ name: ' + index.name +
25 ', keyPath: ' + index.keyPath +
25 ', keyPath: ' + index.keyPath +
@@ -28,7 +28,7 @@ exports._showIndex = function _showIndex(index) {
28 ' })';
28 ' })';
29};
29};
30
30
31−exports._showKeyRange = function _showKeyRange(range) {
31+export function _showKeyRange(range) {
32 return '(IDBKeyRange ' +
32 return '(IDBKeyRange ' +
33 '{ lower: ' + range.lower +
33 '{ lower: ' + range.lower +
34 ', upper: ' + range.upper +
34 ', upper: ' + range.upper +
@@ -37,7 +37,7 @@ exports._showKeyRange = function _showKeyRange(range) {
37 ' })';
37 ' })';
38};
38};
39
39
40−exports._showObjectStore = function _showObjectStore(store) {
40+export function _showObjectStore(store) {
41 return '(IDBObjectStore ' +
41 return '(IDBObjectStore ' +
42 '{ autoIncrement: ' + store.autoIncrement +
42 '{ autoIncrement: ' + store.autoIncrement +
43 ', indexNames: [' + toArray(store.indexNames).join(', ') + ']' +
43 ', indexNames: [' + toArray(store.indexNames).join(', ') + ']' +
@@ -46,7 +46,7 @@ exports._showObjectStore = function _showObjectStore(store) {
46 ' })';
46 ' })';
47};
47};
48
48
49−exports._showTransaction = function _showTransaction(tx) {
49+export function _showTransaction(tx) {
50 return '(IDBTransaction ' +
50 return '(IDBTransaction ' +
51 '{ error: ' + tx.error +
51 '{ error: ' + tx.error +
52 ', mode: ' + tx.mode +
52 ', mode: ' + tx.mode +
src/Database/IndexedDB/Core.purs+163−58
@@ -1,137 +1,242 @@
1+-- | The Core module gathers types used across the library and provides basic Show instances for
2+-- | those types.
1module Database.IndexedDB.Core
3module Database.IndexedDB.Core
2− ( class FromString, parse
4+ -- * Effects
3− , INDEXED_DB
5+ (
4− , IDB
6+ -- IDB
5− , CursorDirection(..)
7+
6− , CursorSource(..)
8+ -- * Types
7− , Database
9+ Database
8 , Index
10 , Index
9 , KeyCursor
11 , KeyCursor
10 , KeyRange
12 , KeyRange
11− , KeyPath
12 , ObjectStore
13 , ObjectStore
13 , Transaction
14 , Transaction
14− , TransactionMode(..)
15 , ValueCursor
15 , ValueCursor
16+ , CursorDirection(..)
17+ , CursorSource(..)
18+ , KeyPath
19+ , TransactionMode(..)
20+
21+ -- * Classes
22+ , class IDBConcreteCursor
23+ , class IDBCursor
24+ , class IDBDatabase
25+ , class IDBIndex
26+ , class IDBKeyRange
27+ , class IDBTransaction
28+ , class IDBObjectStore
29+
30+ -- * Re-Exports
16 , module Database.IndexedDB.IDBKey
31 , module Database.IndexedDB.IDBKey
17 ) where
32 ) where
18
33
19−import Prelude (class Show)
34+import Prelude (class Show)
20
35
21−import Control.Monad.Eff (kind Effect)
36+import Data.Maybe (Maybe(..))
22−import Data.Maybe (Maybe(..))
37+import Data.String.Read (class Read)
23
38
24import Database.IndexedDB.IDBKey
39import Database.IndexedDB.IDBKey
25
40
41+--------------------
42+-- TYPES
43+--
26
44
45+-- | Each origin has an associated set of databases. A database has zero or more object
46+-- | stores which hold the data stored in the database.
27−foreign import data INDEXED_DB :: Effect
47+foreign import data Database :: Type
28
48
49+-- | An index allows looking up records in an object store using properties of the values
50+-- | in the object stores records.
51+foreign import data Index :: Type
29
52
53+-- | A key range is a continuous interval over some data type used for keys.
30−foreign import data IDB :: Effect
54+foreign import data KeyRange :: Type
31
55
56+-- | A cursor is used to iterate over a range of records in an index or an object store
57+-- | in a specific direction. A KeyCursor doesn't hold any value.
58+foreign import data KeyCursor :: Type
32
59
33−data CursorDirection = Next | NextUnique | Prev | PrevUnique
60+-- | A Transaction is used to interact with the data in a database.
61+-- | Whenever data is read or written to the database it is done by using a transaction.
62+foreign import data Transaction :: Type
34
63
64+-- | An object store is the primary storage mechanism for storing data in a database.
65+foreign import data ObjectStore :: Type
35
66
36−data CursorSource = ObjectStore ObjectStore | Index Index
67+-- | A cursor is used to iterate over a range of records in an index or an object store
68+-- | in a specific direction. A ValueCursor also holds the value corresponding to matching key.
69+foreign import data ValueCursor :: Type
37
70
71+-- | A cursor has a direction that determines whether it moves in monotonically
72+-- | increasing or decreasing order of the record keys when iterated, and if it
73+-- | skips duplicated values when iterating indexes.
74+-- | The direction of a cursor also determines if the cursor initial position is at
75+-- | the start of its source or at its end.
76+data CursorDirection
77+ = Next
78+ | NextUnique
79+ | Prev
80+ | PrevUnique
38
81
39−data TransactionMode = ReadOnly | ReadWrite | VersionChange
82+-- | If the source of a cursor is an object store, the effective object store of
83+-- | the cursor is that object store and the effective key of the cursor is the
84+-- | cursor’s position. If the source of a cursor is an index, the effective object
85+-- | store of the cursor is that index’s referenced object store and the effective key
86+-- | is the cursor’s object store position.
87+data CursorSource
88+ = ObjectStore ObjectStore
89+ | Index Index
40
90
91+-- | A key path is a list of strings that defines how to extract a key from a value.
92+-- | A valid key path is one of:
93+-- |
94+-- | - An empty list.
95+-- | - An singleton identifier, which is a string matching the IdentifierName production from the ECMAScript Language Specification [ECMA-262].
96+-- | - A singleton string consisting of two or more identifiers separated by periods (U+002E FULL STOP).
97+-- | - A non-empty list containing only strings conforming to the above requirements.
98+type KeyPath = Array String
41
99
42−type KeyPath = Array String
100+-- | A transaction has a mode that determines which types of interactions can be performed
101+-- | upon that transaction. The mode is set when the transaction is created and remains
102+-- | fixed for the life of the transaction.
103+data TransactionMode
104+ = ReadOnly -- ^ The transaction is only allowed to read data.
105+ | ReadWrite -- ^ The transaction is allowed to read, modify and delete data from existing object stores
106+ | VersionChange -- ^ The transaction is allowed to read, modify and delete data from existing object stores, and can also create and remove object stores and indexes.
43
107
108+--------------------
109+-- Classes
110+--
44
111
45−foreign import data Database :: Type
112+-- | A concrete cursor not only shares IDBCursor properties, but also some
113+-- | specific attributes (see KeyCursor or CursorWithValue).
114+class (IDBCursor cursor) <= IDBConcreteCursor cursor
46
115
116+-- | Cursor objects implement the IDBCursor interface.
117+-- | There is only ever one IDBCursor instance representing a given cursor.
118+-- | There is no limit on how many cursors can be used at the same time.
119+class IDBCursor cursor
47
120
48−foreign import data Index :: Type
121+-- | The IDBDatabase interface represents a connection to a database.
122+class IDBDatabase db
49
123
124+-- | The IDBIndex interface represents an index handle.
125+-- | Any of these methods throw an "TransactionInactiveError" DOMException
126+-- | if called when the transaction is not active.
127+class IDBIndex index
50
128
51−foreign import data KeyCursor :: Type
129+-- | The IDBKeyRange interface represents a key range.
130+class IDBKeyRange range
52
131
132+-- | The IDBtransaction interface.
133+class IDBTransaction tx
53
134
54−foreign import data KeyRange :: Type
135+-- | The IDBObjectStore interface represents an object store handle.
136+class IDBObjectStore store
55
137
138+--------------------
139+-- INSTANCES
140+--
56
141
57−foreign import data ObjectStore :: Type
142+instance idbCursorKeyCursor :: IDBCursor KeyCursor
58
143
144+instance idbCursorValueCursor :: IDBCursor ValueCursor
59
145
60−foreign import data Transaction :: Type
146+instance idbConcreteCursorKeyCursor :: IDBConcreteCursor KeyCursor
61
147
148+instance idbConcreteCursorValueCursor :: IDBConcreteCursor ValueCursor
62
149
63−foreign import data ValueCursor :: Type
150+instance idbDatabaseDatabase :: IDBDatabase Database
151+
152+instance idbIndexIndex :: IDBIndex Index
153+
154+instance idbIndexObjectStore :: IDBIndex ObjectStore
64
155
156+instance idbKeyRangeKeyRange :: IDBKeyRange KeyRange
65
157
66−class FromString a where
158+instance idbObjectStoreObjectStore :: IDBObjectStore ObjectStore
67− parse :: String -> Maybe a
68
159
160+instance idbTransactionTransaction :: IDBTransaction Transaction
69
161
70−foreign import _showCursor :: forall cursor. cursor -> String
71instance showKeyCursor :: Show KeyCursor where
162instance showKeyCursor :: Show KeyCursor where
72 show = _showCursor
163 show = _showCursor
73
164
74−
75instance showValueCursor :: Show ValueCursor where
165instance showValueCursor :: Show ValueCursor where
76 show = _showCursor
166 show = _showCursor
77
167
78−
79−foreign import _showDatabase :: Database -> String
80−instance showDatabase :: Show Database where
81− show = _showDatabase
82−
83−
84−foreign import _showIndex :: Index -> String
85instance showIndex :: Show Index where
168instance showIndex :: Show Index where
86 show = _showIndex
169 show = _showIndex
87
170
88−
89−foreign import _showKeyRange :: KeyRange -> String
90instance showKeyRange :: Show KeyRange where
171instance showKeyRange :: Show KeyRange where
91 show = _showKeyRange
172 show = _showKeyRange
92
173
93−
94−foreign import _showObjectStore :: ObjectStore -> String
95instance showObjectStore :: Show ObjectStore where
174instance showObjectStore :: Show ObjectStore where
96 show = _showObjectStore
175 show = _showObjectStore
97
176
98−
99−foreign import _showTransaction :: Transaction -> String
100instance showTransaction :: Show Transaction where
177instance showTransaction :: Show Transaction where
101 show = _showTransaction
178 show = _showTransaction
102
179
180+instance showDatabase :: Show Database where
181+ show = _showDatabase
103
182
104instance showCursorDirection :: Show CursorDirection where
183instance showCursorDirection :: Show CursorDirection where
105 show x =
184 show x =
106 case x of
185 case x of
107− Next -> "next"
186+ Next -> "next"
108 NextUnique -> "nextunique"
187 NextUnique -> "nextunique"
109− Prev -> "prev"
188+ Prev -> "prev"
110 PrevUnique -> "prevunique"
189 PrevUnique -> "prevunique"
111
190
112−
113instance showTransactionMode :: Show TransactionMode where
191instance showTransactionMode :: Show TransactionMode where
114 show x =
192 show x =
115 case x of
193 case x of
116− ReadOnly -> "readonly"
194+ ReadOnly -> "readonly"
117− ReadWrite -> "readwrite"
195+ ReadWrite -> "readwrite"
118 VersionChange -> "versionchange"
196 VersionChange -> "versionchange"
119
197
120−
121−instance fromStringCursorDirection :: FromString CursorDirection where
198+instance readCursorDirection :: Read CursorDirection where
122− parse s =
199+ read s =
123 case s of
200 case s of
124− "next" -> Just Next
201+ "next" -> Just Next
125 "nextunique" -> Just NextUnique
202 "nextunique" -> Just NextUnique
126− "prev" -> Just Prev
203+ "prev" -> Just Prev
127 "prevunique" -> Just PrevUnique
204 "prevunique" -> Just PrevUnique
128− _ -> Nothing
205+ _ -> Nothing
129−
130
206
131−instance fromStringTransactionMode :: FromString TransactionMode where
207+instance readTransactionMode :: Read TransactionMode where
132− parse s =
208+ read s =
133 case s of
209 case s of
134− "readonly" -> Just ReadOnly
210+ "readonly" -> Just ReadOnly
135− "readwrite" -> Just ReadWrite
211+ "readwrite" -> Just ReadWrite
136 "versionchange" -> Just VersionChange
212 "versionchange" -> Just VersionChange
137− _ -> Nothing
213+ _ -> Nothing
214+
215+--------------------
216+-- FFI
217+--
218+
219+foreign import _showCursor
220+ :: forall cursor
221+ . cursor
222+ -> String
223+
224+foreign import _showDatabase
225+ :: Database
226+ -> String
227+
228+foreign import _showIndex
229+ :: Index
230+ -> String
231+
232+foreign import _showKeyRange
233+ :: KeyRange
234+ -> String
235+
236+foreign import _showObjectStore
237+ :: ObjectStore
238+ -> String
239+
240+foreign import _showTransaction
241+ :: Transaction
242+ -> String
src/Database/IndexedDB/IDBCursor.js+95−32
@@ -1,6 +1,6 @@
1const errorHandler = function errorHandler(cb) {
1const errorHandler = function errorHandler(cb) {
2 return function _handler(e) {
2 return function _handler(e) {
3− cb(new Error(e.target.error.name));
3+ cb(e.target.error);
4 };
4 };
5};
5};
6
6
@@ -10,72 +10,135 @@ const successHandler = function successHandler(cb) {
10 };
10 };
11};
11};
12
12
13−
14−exports._advance = function _advance(cursor, count) {
13+export function _advance(cursor, count) {
15− return function eff() {
14+ return function aff(error, success) {
16 try {
15 try {
17 cursor.advance(count);
16 cursor.advance(count);
17+ success();
18 } catch (e) {
18 } catch (e) {
19− throw new Error(e.name);
19+ error(e);
20 }
20 }
21+
22+ return function canceler(_, cancelerError) {
23+ cancelerError(new Error("Can't cancel IDB Effects"));
24+ };
21 };
25 };
22};
26};
23
27
24−exports._continue = function _continue(cursor, key) {
28+export function _continue(cursor, key) {
25− return function eff() {
29+ return function aff(error, success) {
26 try {
30 try {
27 cursor.continue(key || undefined);
31 cursor.continue(key || undefined);
32+ success();
28 } catch (e) {
33 } catch (e) {
29− throw new Error(e.name);
34+ error(e);
30 }
35 }
36+
37+ return function canceler(_, cancelerError) {
38+ cancelerError(new Error("Can't cancel IDB Effects"));
39+ };
31 };
40 };
32};
41};
33
42
34−exports._continuePrimaryKey = function _continuePrimaryKey(cursor, key, primaryKey) {
43+export function _continuePrimaryKey(cursor, key, primaryKey) {
35− return function eff() {
44+ return function aff(error, success) {
36 try {
45 try {
37 cursor.continuePrimaryKey(key, primaryKey);
46 cursor.continuePrimaryKey(key, primaryKey);
47+ success();
38 } catch (e) {
48 } catch (e) {
39− throw new Error(e.name);
49+ error(e);
40 }
50 }
51+
52+ return function canceler(_, cancelerError) {
53+ cancelerError(new Error("Can't cancel IDB Effects"));
54+ };
41 };
55 };
42};
56};
43
57
44−exports._delete = function _delete(cursor) {
58+export function _delete(cursor) {
45− return function aff(success, error) {
59+ return function aff(error, success) {
60+ try {
46− const request = cursor.delete();
61+ const request = cursor.delete();
47− request.onsuccess = successHandler(success);
62+ request.onsuccess = successHandler(success);
48− request.onerror = errorHandler(error);
63+ request.onerror = errorHandler(error);
64+ } catch (e) {
65+ error(e);
66+ }
67+
68+ return function canceler(_, cancelerError) {
69+ cancelerError(new Error("Can't cancel IDB Effects"));
70+ };
49 };
71 };
50};
72};
51
73
52−exports._direction = function _direction(fromString, cursor) {
74+export function _direction(fromString, cursor) {
53 return fromString(cursor.direction);
75 return fromString(cursor.direction);
54};
76};
55
77
56−exports._key = function _key(cursor) {
78+export function _key(cursor) {
79+ return function aff(error, success) {
80+ try {
57− return cursor.key;
81+ success(cursor.key);
82+ } catch (e) {
83+ error(e);
84+ }
85+
86+ return function canceler(_, cancelerError) {
87+ cancelerError(new Error("Can't cancel IDB Effects"));
88+ };
89+ };
58};
90};
59
91
60−exports._primaryKey = function _primaryKey(cursor) {
92+export function _primaryKey(cursor) {
93+ return function aff(error, success) {
94+ try {
61− return cursor.primaryKey;
95+ success(cursor.primaryKey);
96+ } catch (e) {
97+ error(e);
98+ }
99+
100+ return function canceler(_, cancelerError) {
101+ cancelerError(new Error("Can't cancel IDB Effects"));
102+ };
103+ };
62};
104};
63
105
64−exports._source = function _source(IDBObjectStore, IDBIndex, cursor) {
106+export function _source(IDBObjectStore, IDBIndex, cursor) {
65− console.log(typeof cursor.source);
107+ switch (cursor.source.constructor.name) {
66− console.log(Object.getPrototypeOf(cursor.source));
108+ case 'IDBIndex':
109+ return IDBIndex(cursor.source);
110+ case 'IDBObjectStore':
67− console.log(cursor.source);
111+ return IDBObjectStore(cursor.source);
112+ default:
68− throw new Error('TODO');
113+ throw Object.create(Error.prototype, {
114+ message: {
115+ enumerable: true,
116+ value: 'Unable to retrieve the cursor\'s source constructor.',
117+ },
118+ name: {
119+ enumerable: true,
120+ value: 'UnexpectedCursorSource',
121+ },
122+ });
123+ }
69};
124};
70
125
71−exports._update = function _update(cursor, value) {
126+export function _update(cursor, value) {
72− return function aff(success, error) {
127+ return function aff(error, success) {
128+ try {
73− const request = cursor.update(value);
129+ const request = cursor.update(value);
74− request.onsuccess = successHandler(success);
130+ request.onsuccess = successHandler(success);
75− request.onerror = errorHandler(error);
131+ request.onerror = errorHandler(error);
132+ } catch (e) {
133+ error(e);
134+ }
135+
136+ return function canceler(_, cancelerError) {
137+ cancelerError(new Error("Can't cancel IDB Effects"));
138+ };
76 };
139 };
77};
140};
78
141
79−exports._value = function _value(cursor) {
142+export function _value(cursor) {
80 return cursor.value;
143 return cursor.value;
81};
144};
src/Database/IndexedDB/IDBCursor.purs+154−108
@@ -1,140 +1,186 @@
1+-- | A cursor is used to iterate over a range of records in an index or
2+-- | an object store in a specific direction.
1module Database.IndexedDB.IDBCursor
3module Database.IndexedDB.IDBCursor
4+ -- * Interface
5+ ( advance
6+ , continue
2− ( class IDBCursor, advance, continue, continuePrimaryKey, delete, update
7+ , continuePrimaryKey
8+ , delete
9+ , update
10+
11+ -- * Attributes
3 , direction
12 , direction
4 , key
13 , key
5 , primaryKey
14 , primaryKey
6 , source
15 , source
7− , direction'
8− , key'
9− , primaryKey'
10− , source'
11 , value
16 , value
12 ) where
17 ) where
13
18
14−import Prelude (Unit, (>>>))
15−
16−import Control.Monad.Aff (Aff)
17−import Control.Monad.Eff (Eff)
18−import Control.Monad.Eff.Exception (EXCEPTION)
19−import Data.Function.Uncurried as Fn
20−import Data.Function.Uncurried (Fn2, Fn3)
21−import Data.Maybe (Maybe)
22−import Data.Nullable (Nullable, toNullable)
23−import Data.Foreign (Foreign, toForeign, unsafeFromForeign)
24−
25import Database.IndexedDB.Core
19import Database.IndexedDB.Core
26
20
21+import Data.Function.Uncurried (Fn2, Fn3)
22+import Data.Function.Uncurried as Fn
23+import Data.Maybe (Maybe)
24+import Data.Nullable (Nullable, toNullable)
25+import Data.String.Read (read)
26+import Database.IndexedDB.IDBKey.Internal (class IDBKey, Key, toKey, unsafeFromKey)
27+import Effect.Aff (Aff)
28+import Effect.Aff.Compat (EffectFnAff, fromEffectFnAff)
29+import Foreign (Foreign, unsafeFromForeign, unsafeToForeign)
30+import Prelude (Unit, ($), (>>>), (<<<), map)
27
31
28--------------------
32--------------------
29-- INTERFACES
33-- INTERFACES
30--
34--
31−class IDBCursor cursor where
32− advance :: forall e. cursor -> Int -> Eff (idb :: IDB, exception :: EXCEPTION | e) Unit
33− continue :: forall e. cursor -> Maybe Key -> Eff (idb :: IDB, exception :: EXCEPTION | e) Unit
34− continuePrimaryKey :: forall e. cursor -> Key -> Key -> Eff (idb :: IDB, exception :: EXCEPTION | e) Unit
35− delete :: forall e. cursor -> Aff (idb ::IDB, exception :: EXCEPTION | e) Unit
36− update :: forall val e. cursor -> val -> Aff (idb :: IDB, exception :: EXCEPTION | e) Key
37
35
36+-- | Advances the cursor through the next count records in range.
37+advance
38+ :: forall cursor
39+ . (IDBCursor cursor)
40+ => cursor
41+ -> Int
42+ -> Aff Unit
43+advance c =
44+ fromEffectFnAff <<< Fn.runFn2 _advance c
45+
46+-- | Advances the cursor to the next record in range matching or after key.
47+continue
48+ :: forall k cursor
49+ . (IDBKey k)
50+ => (IDBCursor cursor)
51+ => cursor
52+ -> Maybe k
53+ -> Aff Unit
54+continue c mk =
55+ fromEffectFnAff $ Fn.runFn2 _continue c (toNullable $ map (toKey >>> unsafeFromKey) mk)
56+
57+-- | Advances the cursor to the next record in range matching or after key and primaryKey. Throws an "InvalidAccessError" DOMException if the source is not an index.
58+continuePrimaryKey
59+ :: forall k cursor
60+ . (IDBKey k)
61+ => (IDBCursor cursor)
62+ => cursor
63+ -> k
64+ -> k
65+ -> Aff Unit
66+continuePrimaryKey c k1 k2 =
67+ fromEffectFnAff $ Fn.runFn3 _continuePrimaryKey c (unsafeFromKey $ toKey k1) (unsafeFromKey $ toKey k2)
68+
69+-- | Delete the record pointed at by the cursor with a new value.
70+delete
71+ :: forall cursor
72+ . (IDBCursor cursor)
73+ => cursor
74+ -> Aff Unit
75+delete =
76+ fromEffectFnAff <<< _delete
77+
78+-- | Update the record pointed at by the cursor with a new value.
79+-- |
80+-- | Throws a "DataError" DOMException if the effective object store uses
81+-- | in-line keys and the key would have changed.
82+update
83+ :: forall val cursor
84+ . (IDBCursor cursor)
85+ => cursor
86+ -> val
87+ -> Aff Key
88+update c =
89+ map toKey <<< fromEffectFnAff <<< Fn.runFn2 _update c <<< unsafeToForeign
38
90
39--------------------
91--------------------
40-- ATTRIBUTES
92-- ATTRIBUTES
41--
93--
42−direction :: KeyCursor -> CursorDirection
43−direction =
44− Fn.runFn2 _direction (parse >>> toNullable)
45
94
46−
95+-- | Returns the direction (Next|NextUnique|Prev|PrevUnique) of the cursor.
47−key :: KeyCursor -> Key
96+direction
97+ :: forall cursor
98+ . (IDBConcreteCursor cursor)
99+ => cursor
100+ -> CursorDirection
101+direction =
102+ Fn.runFn2 _direction (read >>> toNullable)
103+
104+-- | Returns the key of the cursor. Throws a "InvalidStateError" DOMException
105+-- | if the cursor is advancing or is finished.
106+key
107+ :: forall cursor
108+ . (IDBConcreteCursor cursor)
109+ => cursor
110+ -> Aff Key
48key =
111key =
49− _key
112+ map toKey <<< fromEffectFnAff <<< _key
50−
113+
51−
114+-- | Returns the effective key of the cursor. Throws a "InvalidStateError" DOMException
115+-- | if the cursor is advancing or is finished.
52−primaryKey :: KeyCursor -> Key
116+primaryKey
117+ :: forall cursor
118+ . (IDBConcreteCursor cursor)
119+ => cursor
120+ -> Aff Key
53primaryKey =
121primaryKey =
54− _primaryKey
122+ map toKey <<< fromEffectFnAff <<< _primaryKey
55−
123+
56−
124+-- | Returns the IDBObjectStore or IDBIndex the cursor was opened from.
125+source
126+ :: forall cursor
127+ . (IDBConcreteCursor cursor)
128+ => cursor
57−source :: KeyCursor -> CursorSource
129+ -> CursorSource
58source =
130source =
59 Fn.runFn3 _source ObjectStore Index
131 Fn.runFn3 _source ObjectStore Index
60
132
61−
133+value
62−-- NOTE: Polymorphism not used here cause *we want* those methods to be distincs for
134+ :: forall val
63−-- the concrete types mostly for consistency with the Database.IndexedDB.IDBIndex module
64−-- where the IDBIndex interfaces is shared by Index and ObjectStore, but attributes of
65−-- respective concrete types aren't shared.
66−direction' :: ValueCursor -> CursorDirection
67−direction' =
68− Fn.runFn2 _direction (parse >>> toNullable)
69−
70−
71−key' :: ValueCursor -> Key
72−key' =
73− _key
74−
75−
76−primaryKey' :: ValueCursor -> Key
77−primaryKey' =
78− _primaryKey
79−
80−
81−source' :: ValueCursor -> CursorSource
82−source' =
83− Fn.runFn3 _source ObjectStore Index
84−
85−
86−value :: forall val. ValueCursor -> val
135+ . ValueCursor
136+ -> val
87value =
137value =
88 _value >>> unsafeFromForeign
138 _value >>> unsafeFromForeign
89
139
90−
91−--------------------
92−-- INSTANCES
93−--
94−instance keyCursorKeyCursor :: IDBCursor KeyCursor where
95− advance = Fn.runFn2 _advance
96− continue c mk = Fn.runFn2 _continue c (toNullable mk)
97− continuePrimaryKey = Fn.runFn3 _continuePrimaryKey
98− delete = _delete
99− update c = toForeign >>> Fn.runFn2 _update c
100−
101−
102−instance valueCursorKeyCursor :: IDBCursor ValueCursor where
103− advance = Fn.runFn2 _advance
104− continue c mk = Fn.runFn2 _continue c (toNullable mk)
105− continuePrimaryKey = Fn.runFn3 _continuePrimaryKey
106− delete = _delete
107− update c = toForeign >>> Fn.runFn2 _update c
108−
109−
110--------------------
140--------------------
111-- FFI
141-- FFI
112--
142--
113−foreign import _advance :: forall cursor e. Fn2 cursor Int (Eff (idb :: IDB, exception :: EXCEPTION | e) Unit)
114−
115−
116−foreign import _continue :: forall cursor e. Fn2 cursor (Nullable Key) (Eff (idb :: IDB, exception :: EXCEPTION | e) Unit)
117−
118−
119−foreign import _continuePrimaryKey :: forall cursor e. Fn3 cursor Key Key (Eff (idb :: IDB, exception :: EXCEPTION | e) Unit)
120−
121−
122−foreign import _delete :: forall cursor e. cursor -> (Aff (idb ::IDB, exception :: EXCEPTION | e) Unit)
123−
124−
125−foreign import _direction :: forall cursor. Fn2 (String -> Nullable CursorDirection) cursor CursorDirection
126−
127−
128−foreign import _key :: forall cursor. cursor -> Key
129−
130−
131−foreign import _primaryKey :: forall cursor. cursor -> Key
132−
133−
134−foreign import _source :: forall cursor. Fn3 (ObjectStore -> CursorSource) (Index -> CursorSource) cursor CursorSource
135−
136−
137−foreign import _update :: forall cursor e. Fn2 cursor Foreign (Aff (idb :: IDB, exception :: EXCEPTION | e) Key)
138−
139
143
144+foreign import _advance
145+ :: forall cursor
146+ . Fn2 cursor Int (EffectFnAff Unit)
147+
148+foreign import _continue
149+ :: forall cursor
150+ . Fn2 cursor (Nullable Foreign) (EffectFnAff Unit)
151+
152+foreign import _continuePrimaryKey
153+ :: forall cursor
154+ . Fn3 cursor Foreign Foreign (EffectFnAff Unit)
155+
156+foreign import _delete
157+ :: forall cursor
158+ . cursor
159+ -> (EffectFnAff Unit)
160+
161+foreign import _direction
162+ :: forall cursor
163+ . Fn2 (String -> Nullable CursorDirection) cursor CursorDirection
164+
165+foreign import _key
166+ :: forall cursor
167+ . cursor
168+ -> EffectFnAff Key
169+
170+foreign import _primaryKey
171+ :: forall cursor
172+ . cursor
173+ -> EffectFnAff Key
174+
175+foreign import _source
176+ :: forall cursor
177+ . Fn3 (ObjectStore -> CursorSource) (Index -> CursorSource) cursor CursorSource
178+
179+foreign import _update
180+ :: forall cursor
181+ . Fn2 cursor Foreign (EffectFnAff Foreign)
182+
183+foreign import _value
140−foreign import _value :: forall cursor val. cursor -> val
184+ :: forall cursor val
185+ . cursor
186+ -> val
src/Database/IndexedDB/IDBDatabase.js+72−29
@@ -1,20 +1,25 @@
1−const $Core = require('Database.IndexedDB.Core/foreign');
1+const toArray = function toArray(xs) {
2−
2+ return Array.prototype.slice.apply(xs);
3−const toArray = $Core.toArray;
3+};
4
4
5
5
6−exports._close = function _close(db) {
6+export function _close(db) {
7− return function eff() {
7+ return function aff(error, success) {
8 try {
8 try {
9 db.close();
9 db.close();
10+ success();
10 } catch (e) {
11 } catch (e) {
11− throw new Error(e.name);
12+ error(e);
12 }
13 }
14+
15+ return function canceler(_, cancelerError) {
16+ cancelerError(new Error("Can't cancel IDB Effects"));
17+ };
13 };
18 };
14};
19};
15
20
16−exports._createObjectStore = function _createObjectStore(db, name, opts) {
21+export function _createObjectStore(db, name, opts) {
17− return function eff() {
22+ return function aff(error, success) {
18 var keyPath;
23 var keyPath;
19
24
20 try {
25 try {
@@ -31,76 +36,114 @@ exports._createObjectStore = function _createObjectStore(db, name, opts) {
31 default:
36 default:
32 keyPath = opts.keyPath;
37 keyPath = opts.keyPath;
33 }
38 }
39+
34− return db.createObjectStore(name, {
40+ const store = db.createObjectStore(name, {
35 autoIncrement: opts.autoIncrement,
41 autoIncrement: opts.autoIncrement,
36 keyPath: keyPath,
42 keyPath: keyPath,
37 });
43 });
44+ success(store);
38 } catch (e) {
45 } catch (e) {
39− throw new Error(e.name);
46+ error(e);
40 }
47 }
48+
49+ return function canceler(_, cancelerError) {
50+ cancelerError(new Error("Can't cancel IDB Effects"));
51+ };
41 };
52 };
42};
53};
43
54
44−exports._deleteObjectStore = function _deleteObjectStore(db, name) {
55+export function _deleteObjectStore(db, name) {
45− return function eff() {
56+ return function aff(error, success) {
46 try {
57 try {
47 db.deleteObjectStore(name);
58 db.deleteObjectStore(name);
59+ success();
48 } catch (e) {
60 } catch (e) {
49− throw new Error(e.name);
61+ error(e);
50 }
62 }
63+
64+ return function canceler(_, cancelerError) {
65+ cancelerError(new Error("Can't cancel IDB Effects"));
66+ };
51 };
67 };
52};
68};
53
69
54−exports._name = function _name(db) {
70+export function _name(db) {
55 return db.name;
71 return db.name;
56};
72};
57
73
58−exports._objectStoreNames = function _objectStoreNames(db) {
74+export function _objectStoreNames(db) {
59 return toArray(db.objectStoreNames);
75 return toArray(db.objectStoreNames);
60};
76};
61
77
62−exports._onAbort = function _onAbort(db, f) {
78+export function _onAbort(db, f) {
63− return function eff() {
79+ return function aff(error, success) {
64 db.onabort = function onabort() {
80 db.onabort = function onabort() {
65 f();
81 f();
66 };
82 };
83+ success();
84+
85+ return function canceler(_, cancelerError) {
86+ cancelerError(new Error("Can't cancel IDB Effects"));
87+ };
67 };
88 };
68};
89};
69
90
70−exports._onClose = function _onClose(db, f) {
91+export function _onClose(db, f) {
71− return function eff() {
92+ return function aff(error, success) {
72 db.onclose = function onclose() {
93 db.onclose = function onclose() {
73 f();
94 f();
74 };
95 };
96+ success();
97+
98+ return function canceler(_, cancelerError) {
99+ cancelerError(new Error("Can't cancel IDB Effects"));
100+ };
75 };
101 };
76};
102};
77
103
78−exports._onError = function _onError(db, f) {
104+export function _onError(db, f) {
79− return function eff() {
105+ return function aff(error, success) {
80 db.onerror = function onerror(e) {
106 db.onerror = function onerror(e) {
81− f(new Error(e.target.error.name))();
107+ f(e.target.error)();
108+ };
109+ success();
110+
111+ return function canceler(_, cancelerError) {
112+ cancelerError(new Error("Can't cancel IDB Effects"));
82 };
113 };
83 };
114 };
84};
115};
85
116
86−exports._onVersionChange = function _onVersionChange(db, f) {
117+export function _onVersionChange(db, f) {
87− return function eff() {
118+ return function aff(error, success) {
88 db.onversionchange = function onversionchange(e) {
119 db.onversionchange = function onversionchange(e) {
89 f({ oldVersion: e.oldVersion, newVersion: e.newVersion })();
120 f({ oldVersion: e.oldVersion, newVersion: e.newVersion })();
90 };
121 };
122+ success();
123+
124+ return function canceler(_, cancelerError) {
125+ cancelerError(new Error("Can't cancel IDB Effects"));
126+ };
91 };
127 };
92};
128};
93
129
94−exports._transaction = function _transaction(show, db, stores, mode) {
130+export function _transaction(db, stores, mode) {
95− return function eff() {
131+ return function aff(error, success) {
132+ var transaction;
96 try {
133 try {
97− return db.transaction(stores, show(mode));
134+ transaction = db.transaction(stores, mode);
135+ success(transaction);
98 } catch (e) {
136 } catch (e) {
99− throw new Error(e.name);
137+ error(e);
100 }
138 }
139+
140+ return function canceler(_, cancelerError, cancelerSuccess) {
141+ transaction.abort();
142+ cancelerSuccess();
143+ };
101 };
144 };
102};
145};
103
146
104−exports._version = function _version(db) {
147+export function _version(db) {
105 return db.version;
148 return db.version;
106};
149};
src/Database/IndexedDB/IDBDatabase.purs+160−74
@@ -1,129 +1,215 @@
1+-- | Each origin has an associated set of databases. A database has zero or more object
2+-- | stores which hold the data stored in the database.
1module Database.IndexedDB.IDBDatabase
3module Database.IndexedDB.IDBDatabase
4+ -- * Types
5+ ( StoreName
6+ , ObjectStoreParameters
7+ , defaultParameters
8+
9+ -- * Interface
10+ , close
2− ( class IDBDatabase, close, createObjectStore, deleteObjectStore, transaction
11+ , createObjectStore
3− , StoreName
12+ , deleteObjectStore
13+ , transaction
14+
15+ -- * Attributes
4 , name
16 , name
5 , objectStoreNames
17 , objectStoreNames
6 , version
18 , version
19+
20+ -- * Event handlers
7 , onAbort
21 , onAbort
8 , onClose
22 , onClose
9 , onError
23 , onError
10 , onVersionChange
24 , onVersionChange
11 ) where
25 ) where
12
26
13−import Prelude (Unit, show)
14−
15−import Control.Monad.Eff (Eff)
16−import Control.Monad.Eff.Exception (EXCEPTION, Error)
17−import Data.Function.Uncurried as Fn
18−import Data.Function.Uncurried (Fn2, Fn3, Fn4)
19−
20import Database.IndexedDB.Core
27import Database.IndexedDB.Core
21−import Database.IndexedDB.IDBObjectStore (IDBObjectStoreParameters)
22
28
29+import Data.Function.Uncurried (Fn2, Fn3)
30+import Data.Function.Uncurried as Fn
31+import Effect (Effect)
32+import Effect.Aff (Aff)
33+import Effect.Aff.Compat (EffectFnAff, fromEffectFnAff)
34+import Effect.Exception (Error)
35+import Prelude (Unit, show, (<<<), ($))
23
36
24--------------------
37--------------------
25−-- INTERFACE
38+-- TYPES
26--
39--
27−class IDBDatabase db where
28− close :: forall e. db -> Eff (idb :: IDB, exception :: EXCEPTION | e) Unit
29− createObjectStore :: forall e. db -> StoreName -> IDBObjectStoreParameters -> Eff (idb :: IDB, exception :: EXCEPTION | e) ObjectStore
30− deleteObjectStore :: forall e. db -> StoreName -> Eff (idb :: IDB, exception :: EXCEPTION | e) ObjectStore
31− transaction :: forall e. db -> KeyPath -> TransactionMode -> Eff (idb :: IDB, exception :: EXCEPTION | e) Transaction
32−
33
40
41+-- | Type alias for StoreName
34type StoreName = String
42type StoreName = String
35
43
44+-- | Options provided when creating an object store.
45+type ObjectStoreParameters =
46+ { keyPath :: KeyPath
47+ , autoIncrement :: Boolean
48+ }
49+
50+defaultParameters :: ObjectStoreParameters
51+defaultParameters =
52+ { keyPath: []
53+ , autoIncrement: false
54+ }
55+
56+--------------------
57+-- INTERFACE
58+--
59+
60+-- | Closes the connection once all running transactions have finished.
61+close
62+ :: forall db
63+ . (IDBDatabase db)
64+ => db
65+ -> Aff Unit
66+close =
67+ fromEffectFnAff <<< _close
68+
69+-- | Creates a new object store with the given name and options and returns a new IDBObjectStore.
70+-- |
71+-- | Throws a "InvalidStateError" DOMException if not called within an upgrade transaction
72+createObjectStore
73+ :: forall db
74+ . (IDBDatabase db)
75+ => db
76+ -> StoreName
77+ -> ObjectStoreParameters
78+ -> Aff ObjectStore
79+createObjectStore db name' opts =
80+ fromEffectFnAff $ Fn.runFn3 _createObjectStore db name' opts
81+
82+-- | Deletes the object store with the given name.
83+-- |
84+-- | Throws a "InvalidStateError" DOMException if not called within an upgrade transaction.
85+deleteObjectStore
86+ :: forall db
87+ . (IDBDatabase db)
88+ => db
89+ -> StoreName
90+ -> Aff ObjectStore
91+deleteObjectStore db name' =
92+ fromEffectFnAff $ Fn.runFn2 _deleteObjectStore db name'
93+
94+-- | Returns a new transaction with the given mode (ReadOnly|ReadWrite)
95+-- | and scope which in the form of an array of object store names.
96+transaction
97+ :: forall db
98+ . (IDBDatabase db)
99+ => db
100+ -> Array StoreName
101+ -> TransactionMode
102+ -> Aff Transaction
103+transaction db stores mode' =
104+ fromEffectFnAff $ Fn.runFn3 _transaction db stores (show mode')
36
105
37--------------------
106--------------------
38-- ATTRIBUTES
107-- ATTRIBUTES
39--
108--
109+
110+-- | Returns the name of the database.
111+name
40−name :: Database -> String
112+ :: Database
113+ -> String
41name =
114name =
42 _name
115 _name
43
116
44−
117+-- | Returns a list of the names of object stores in the database.
45−objectStoreNames :: Database -> Array String
118+objectStoreNames
119+ :: Database
120+ -> Array String
46objectStoreNames =
121objectStoreNames =
47 _objectStoreNames
122 _objectStoreNames
48
123
49−
124+-- | Returns the version of the database.
125+version
50−version :: Database -> Int
126+ :: Database
127+ -> Int
51version =
128version =
52 _version
129 _version
53
130
54−
55--------------------
131--------------------
56-- EVENT HANDLERS
132-- EVENT HANDLERS
57--
133--
58−onAbort :: forall e e'. Database -> Eff ( | e') Unit -> Eff (idb :: IDB | e) Unit
134+-- | Event handler for the `abort` event.
135+onAbort
136+ :: Database
137+ -> Effect Unit
138+ -> Aff Unit
59onAbort db f =
139onAbort db f =
60− Fn.runFn2 _onAbort db f
140+ fromEffectFnAff $ Fn.runFn2 _onAbort db f
61
141
62−
142+-- | Event handler for the `close` event.
63−onClose :: forall e e'. Database -> Eff ( | e') Unit -> Eff (idb :: IDB | e) Unit
143+onClose
144+ :: Database
145+ -> Effect Unit
146+ -> Aff Unit
64onClose db f =
147onClose db f =
65− Fn.runFn2 _onClose db f
148+ fromEffectFnAff $ Fn.runFn2 _onClose db f
66−
67
149
68−onError :: forall e e'. Database -> (Error -> Eff ( | e') Unit) -> Eff (idb :: IDB | e) Unit
150+-- | Event handler for the `error` event.
151+onError
152+ :: Database
153+ -> (Error -> Effect Unit)
154+ -> Aff Unit
69onError db f =
155onError db f =
70− Fn.runFn2 _onError db f
156+ fromEffectFnAff $ Fn.runFn2 _onError db f
71−
157+
72−
158+-- | Event handler for the `versionchange` event.
159+onVersionChange
160+ :: Database
73−onVersionChange :: forall e e'. Database -> ({ oldVersion :: Int, newVersion :: Int } -> Eff ( | e') Unit) -> Eff (idb :: IDB | e) Unit
161+ -> ( { oldVersion :: Int, newVersion :: Int }
162+ -> Effect Unit
163+ )
164+ -> Aff Unit
74onVersionChange db f =
165onVersionChange db f =
75− Fn.runFn2 _onVersionChange db f
166+ fromEffectFnAff $ Fn.runFn2 _onVersionChange db f
76−
77−
78−
79−--------------------
80−-- INSTANCES
81−--
82−instance idbDatabaseDatabase :: IDBDatabase Database where
83− close =
84− _close
85−
86− createObjectStore db name' opts =
87− Fn.runFn3 _createObjectStore db name' opts
88−
89− deleteObjectStore db name' =
90− Fn.runFn2 _deleteObjectStore db name'
91−
92− transaction db stores mode' =
93− Fn.runFn4 _transaction show db stores mode'
94−
95
167
96--------------------
168--------------------
97-- FFI
169-- FFI
98--
170--
99−foreign import _close :: forall db e. db -> Eff (idb :: IDB, exception :: EXCEPTION | e) Unit
100−
101−
102−foreign import _createObjectStore :: forall db e. Fn3 db String { keyPath :: Array String, autoIncrement :: Boolean } (Eff (idb :: IDB, exception :: EXCEPTION | e) ObjectStore)
103−
104−
105−foreign import _deleteObjectStore :: forall db e. Fn2 db String (Eff (idb :: IDB, exception :: EXCEPTION | e) ObjectStore)
106−
107−
108−foreign import _name :: Database -> String
109−
110−
111−foreign import _objectStoreNames :: Database -> Array String
112−
113
171
114−foreign import _onAbort :: forall db e e'. Fn2 db (Eff ( | e') Unit) (Eff (idb :: IDB | e) Unit)
172+foreign import _close
173+ :: forall db
174+ . db
175+ -> EffectFnAff Unit
115
176
177+foreign import _createObjectStore
178+ :: forall db
179+ . Fn3 db String { keyPath :: Array String, autoIncrement :: Boolean } (EffectFnAff ObjectStore)
116
180
117−foreign import _onClose :: forall db e e'. Fn2 db (Eff ( | e') Unit) (Eff (idb :: IDB | e) Unit)
181+foreign import _deleteObjectStore
182+ :: forall db
183+ . Fn2 db String (EffectFnAff ObjectStore)
118
184
185+foreign import _name
186+ :: Database
187+ -> String
119
188
120−foreign import _onError :: forall db e e'. Fn2 db (Error -> Eff ( | e') Unit) (Eff (idb :: IDB | e) Unit)
189+foreign import _objectStoreNames
190+ :: Database
191+ -> Array String
121
192
193+foreign import _onAbort
194+ :: forall db
195+ . Fn2 db (Effect Unit) (EffectFnAff Unit)
122
196
123−foreign import _onVersionChange :: forall db e e'. Fn2 db ({ oldVersion :: Int, newVersion :: Int } -> Eff ( | e') Unit) (Eff (idb :: IDB | e) Unit)
197+foreign import _onClose
198+ :: forall db
199+ . Fn2 db (Effect Unit) (EffectFnAff Unit)
124
200
201+foreign import _onError
202+ :: forall db
203+ . Fn2 db (Error -> Effect Unit) (EffectFnAff Unit)
125
204
126−foreign import _transaction :: forall db e. Fn4 (db -> String) db (Array String) TransactionMode (Eff (idb :: IDB, exception :: EXCEPTION | e) Transaction)
205+foreign import _onVersionChange
206+ :: forall db
207+ . Fn2 db ({ oldVersion :: Int, newVersion :: Int } -> Effect Unit) (EffectFnAff Unit)
127
208
209+foreign import _transaction
210+ :: forall db
211+ . Fn3 db (Array String) String (EffectFnAff Transaction)
128
212
129−foreign import _version :: Database -> Int
213+foreign import _version
214+ :: Database
215+ -> Int
src/Database/IndexedDB/IDBFactory.js+42−21
@@ -1,6 +1,6 @@
1const errorHandler = function errorHandler(cb) {
1const errorHandler = function errorHandler(cb) {
2 return function _handler(e) {
2 return function _handler(e) {
3− cb(new Error(e.target.error.name));
3+ cb(e.target.error);
4 };
4 };
5};
5};
6
6
@@ -14,36 +14,57 @@ const noOp2 = function noOp2() {
14 return noOp;
14 return noOp;
15};
15};
16
16
17+const noOp3 = function noOp3() {
18+ return noOp2;
19+};
17
20
18−exports._deleteDatabase = function _deleteDatabase(name) {
21+export function _deleteDatabase(name) {
19− return function aff(success, error) {
22+ return function aff(error, success) {
23+ try {
20− const request = indexedDB.deleteDatabase(name);
24+ const request = indexedDB.deleteDatabase(name);
21
25
22− request.onsuccess = function onSuccess(e) {
26+ request.onsuccess = function onSuccess(e) {
23− success(e.oldVersion);
27+ success(e.oldVersion);
24− };
28+ };
29+
30+ request.onerror = errorHandler(error);
31+ } catch (e) {
32+ error(e);
33+ }
25
34
26− request.onerror = errorHandler(error);
35+ return function canceler(_, cancelerError) {
36+ cancelerError(new Error("Can't cancel IDB Effects"));
37+ };
27 };
38 };
28};
39};
29
40
30−exports._open = function _open(fromMaybe, name, mver, req) {
41+export function _open(fromMaybe, name, mver, req) {
31 const ver = fromMaybe(undefined)(mver);
42 const ver = fromMaybe(undefined)(mver);
32
43
33− return function aff(success, error) {
44+ return function aff(error, success) {
45+ try {
34− const request = indexedDB.open(name, ver);
46+ const request = indexedDB.open(name, ver);
35− request.onsuccess = function onSuccess(e) {
47+ request.onsuccess = function onSuccess(e) {
36− success(e.target.result);
48+ success(e.target.result);
37− };
49+ };
38
50
39− request.onblocked = function onBlocked() {
51+ request.onblocked = function onBlocked() {
40− fromMaybe(noOp)(req.onBlocked)();
52+ fromMaybe(noOp)(req.onBlocked)();
41− };
53+ };
42
54
43− request.onupgradeneeded = function onUpgradeNeeded(e) {
55+ request.onupgradeneeded = function onUpgradeNeeded(e) {
56+ const meta = { oldVersion: e.oldVersion };
57+ // eslint-disable-next-line max-len
44− fromMaybe(noOp2)(req.onUpgradeNeeded)(e.target.result)(e.target.transaction)();
58+ fromMaybe(noOp3)(req.onUpgradeNeeded)(e.target.result)(e.target.transaction)(meta)();
45− };
59+ };
46
60
47− request.onerror = errorHandler(error);
61+ request.onerror = errorHandler(error);
62+ } catch (e) {
63+ error(e);
64+ }
65+
66+ return function canceler(_, cancelerError) {
67+ cancelerError(new Error("Can't cancel IDB Effects"));
68+ };
48 };
69 };
49};
70};
src/Database/IndexedDB/IDBFactory.purs+61−21
@@ -1,39 +1,79 @@
1+-- | Database objects are accessed through methods on the IDBFactory interface.
2+-- | A single object implementing this interface is present in the global scope
3+-- | of environments that support Indexed DB operations.
1−module Database.IndexedDB.IDBFactory where
4+module Database.IndexedDB.IDBFactory
5+ -- * Types
6+ ( Callbacks
7+ , DatabaseName
8+ , Version
2
9
3−import Prelude (Unit)
10+ -- * Interface
4−
11+ , deleteDatabase
5−import Control.Monad.Aff (Aff)
12+ , open
6−import Control.Monad.Eff (Eff)
13+ ) where
7−import Data.Function.Uncurried as Fn
8−import Data.Function.Uncurried (Fn4)
9−import Data.Maybe (Maybe, fromMaybe)
10
14
11import Database.IndexedDB.Core
15import Database.IndexedDB.Core
12
16
17+import Data.Function.Uncurried (Fn4)
18+import Data.Function.Uncurried as Fn
19+import Data.Maybe (Maybe, fromMaybe)
20+import Effect (Effect)
21+import Effect.Aff (Aff)
22+import Effect.Aff.Compat (EffectFnAff, fromEffectFnAff)
23+import Prelude (Unit, ($), (<<<))
13
24
14--------------------
25--------------------
15−-- INTERFACE
26+-- TYPES
16--
27--
17−type OpenRequest e =
28+
29+-- Type alias for binding listeners to an initial open action.
30+type Callbacks =
18− { onBlocked :: Maybe (Eff (| e) Unit)
31+ { onBlocked :: Maybe (Effect Unit)
19− , onUpgradeNeeded :: Maybe (Database -> Eff (| e) Unit)
32+ , onUpgradeNeeded :: Maybe (Database -> Transaction -> { oldVersion :: Int } -> Effect Unit)
20 }
33 }
21
34
35+-- | Type alias for DatabaseName.
36+type DatabaseName = String
22
37
23−deleteDatabase :: forall e. String -> Aff (idb :: IDB | e) Int
38+-- | Type alias for Version.
24−deleteDatabase =
39+type Version = Int
25− _deleteDatabase
26
40
41+--------------------
42+-- INTERFACE
43+--
27
44
28−open :: forall e e'. String -> Maybe Int -> OpenRequest e' -> Aff (idb :: IDB | e) Database
45+-- | Attempts to delete the named database. If the database already exists
29−open name mver req =
46+-- | and there are open connections that don’t close in response to a
30− Fn.runFn4 _open fromMaybe name mver req
47+-- | `versionchange` event, the request will be blocked until all they close.
48+deleteDatabase
49+ :: DatabaseName
50+ -> Aff Int
51+deleteDatabase =
52+ fromEffectFnAff <<< _deleteDatabase
31
53
54+-- | Attempts to open a connection to the named database with the specified version.
55+-- | If the database already exists with a lower version and there are open connections
56+-- | that don’t close in response to a versionchange event, the request will be blocked
57+-- | until all they close, then an upgrade will occur. If the database already exists with
58+-- | a higher version the request will fail.
59+-- |
60+-- | When the version isn't provided (`Nothing`), attempts to open a connection to the
61+-- | named database with the current version, or 1 if it does not already exist.
62+open
63+ :: DatabaseName
64+ -> Maybe Version
65+ -> Callbacks
66+ -> Aff Database
67+open name mver req =
68+ fromEffectFnAff $ Fn.runFn4 _open fromMaybe name mver req
32
69
33--------------------
70--------------------
34-- FFI
71-- FFI
35--
72--
36−foreign import _deleteDatabase :: forall e. String -> Aff (idb :: IDB | e) Int
73+foreign import _deleteDatabase
37−
74+ :: String
75+ -> EffectFnAff Int
38
76
77+foreign import _open
78+ :: forall a
39−foreign import _open :: forall a e e'. Fn4 (a -> Maybe a -> a) String (Maybe Int) (OpenRequest e') (Aff (idb :: IDB | e) Database)
79+ . Fn4 (a -> Maybe a -> a) String (Maybe Int) (Callbacks) (EffectFnAff Database)
src/Database/IndexedDB/IDBIndex.jsnew file+171
@@ -0,0 +1,171 @@
1+const errorHandler = function errorHandler(cb) {
2+ return function _handler(e) {
3+ cb(e.target.error);
4+ };
5+};
6+
7+const successHandler = function successHandler(cb) {
8+ return function _handler(e) {
9+ cb(e.target.result);
10+ };
11+};
12+
13+
14+export function _keyPath(index) {
15+ const path = index.keyPath;
16+
17+ if (Array.isArray(path)) {
18+ return path;
19+ }
20+
21+ if (typeof path === 'string' && path !== '') {
22+ return [path];
23+ }
24+
25+ return [];
26+};
27+
28+export function _multiEntry(index) {
29+ return index.multiEntry;
30+};
31+
32+export function _name(index) {
33+ return index.name;
34+};
35+
36+export function _objectStore(index) {
37+ return index.obectStore;
38+};
39+
40+export function _unique(index) {
41+ return index.unique;
42+};
43+
44+export function _count(index, query) {
45+ return function aff(error, success) {
46+ try {
47+ const request = index.count(query);
48+ request.onsuccess = successHandler(success);
49+ request.onerror = errorHandler(error);
50+ } catch (e) {
51+ error(e);
52+ }
53+
54+ return function canceler(_, cancelerError) {
55+ cancelerError(new Error("Can't cancel IDB Effects"));
56+ };
57+ };
58+};
59+
60+export function _get(index, range) {
61+ return function aff(error, success) {
62+ try {
63+ const request = index.get(range);
64+ request.onsuccess = successHandler(success);
65+ request.onerror = errorHandler(error);
66+ } catch (e) {
67+ error(e);
68+ }
69+
70+ return function canceler(_, cancelerError) {
71+ cancelerError(new Error("Can't cancel IDB Effects"));
72+ };
73+ };
74+};
75+
76+/*
77+ * NOTE: Require some additional work. The array (which isn't necessarily a list of
78+ * polymorphic types in js) can't be easily translated to a PureScript array.
79+ *
80+ * However, it may be doable to convert the result to some key / value structure with values of
81+ * different types.
82+export function _getAll(index, query, count) {
83+ return function aff(error, success) {
84+ const request = index.getAll(query, count);
85+ request.onsuccess = successHandler(success);
86+ request.onerror = errorHandler(error);
87+ };
88+};
89+*/
90+
91+export function _getAllKeys(index, range, count) {
92+ return function aff(error, success) {
93+ try {
94+ const request = index.getAllKeys(range, count || undefined);
95+ request.onsuccess = successHandler(success);
96+ request.onerror = errorHandler(error);
97+ } catch (e) {
98+ error(e);
99+ }
100+
101+ return function canceler(_, cancelerError) {
102+ cancelerError(new Error("Can't cancel IDB Effects"));
103+ };
104+ };
105+};
106+
107+export function _getKey(index, range) {
108+ return function aff(error, success) {
109+ try {
110+ const request = index.getKey(range);
111+ request.onsuccess = successHandler(success);
112+ request.onerror = errorHandler(error);
113+ } catch (e) {
114+ error(e);
115+ }
116+
117+ return function canceler(_, cancelerError) {
118+ cancelerError(new Error("Can't cancel IDB Effects"));
119+ };
120+ };
121+};
122+
123+export function _openCursor(index, query, dir, cb) {
124+ return function aff(error, success) {
125+ try {
126+ const request = index.openCursor(query, dir);
127+ request.onsuccess = function onSuccess(e) {
128+ if (e.target.result != null) {
129+ cb.onSuccess(e.target.result)();
130+ } else {
131+ cb.onComplete();
132+ }
133+ };
134+ request.onerror = function onError(e) {
135+ cb.onError(e.target.error);
136+ };
137+ success();
138+ } catch (e) {
139+ error(e);
140+ }
141+
142+ return function canceler(_, cancelerError) {
143+ cancelerError(new Error("Can't cancel IDB Effects"));
144+ };
145+ };
146+};
147+
148+export function _openKeyCursor(index, query, dir, cb) {
149+ return function aff(error, success) {
150+ try {
151+ const request = index.openKeyCursor(query, dir);
152+ request.onsuccess = function onSuccess(e) {
153+ if (e.target.result != null) {
154+ cb.onSuccess(e.target.result)();
155+ } else {
156+ cb.onComplete();
157+ }
158+ };
159+ request.onerror = function onError(e) {
160+ cb.onError(e.target.error);
161+ };
162+ success();
163+ } catch (e) {
164+ error(e);
165+ }
166+
167+ return function canceler(_, cancelerError) {
168+ cancelerError(new Error("Can't cancel IDB Effects"));
169+ };
170+ };
171+};
src/Database/IndexedDB/IDBIndex.purs+199−7
@@ -1,14 +1,206 @@
1+-- | An index allows looking up records in an object store using properties of the values
2+-- | in the object stores records.
1module Database.IndexedDB.IDBIndex
3module Database.IndexedDB.IDBIndex
2− ( module Database.IndexedDB.IDBIndex.Internal
4+ -- * Types
3− ) where
5+ ( Callbacks
6+
7+ -- * Interface
8+ , count
9+ , get
10+ , getAllKeys
11+ , getKey
12+ , openCursor
13+ , openKeyCursor
4
14
5−import Database.IndexedDB.IDBIndex.Internal
15+ -- * Attributes
6− ( class IDBIndex, count, get, getAllKeys, getKey, openCursor, openKeyCursor
7− , IDBIndexParameters
8 , keyPath
16 , keyPath
9 , multiEntry
17 , multiEntry
10 , name
18 , name
11 , objectStore
19 , objectStore
12 , unique
20 , unique
13− , defaultParameters
21+ ) where
14− )
22+
23+import Database.IndexedDB.Core
24+
25+import Data.Function.Uncurried (Fn2, Fn3, Fn4)
26+import Data.Function.Uncurried as Fn
27+import Data.Maybe (Maybe)
28+import Data.Nullable (Nullable, toMaybe, toNullable)
29+import Database.IndexedDB.IDBKey.Internal (Key, toKey)
30+import Effect (Effect)
31+import Effect.Aff (Aff)
32+import Effect.Aff.Compat (EffectFnAff, fromEffectFnAff)
33+import Effect.Exception (Error)
34+import Foreign (Foreign, unsafeFromForeign)
35+import Prelude (Unit, map, show, ($), (>>>))
36+
37+--------------------
38+-- TYPES
39+--
40+
41+-- | Callbacks to manipulate a cursor from an Open*Cursor call
42+type Callbacks cursor =
43+ { onSuccess :: cursor -> Effect Unit
44+ , onError :: Error -> Effect Unit
45+ , onComplete :: Effect Unit
46+ }
47+
48+--------------------
49+-- INTERFACES
50+--
51+
52+-- | Retrieves the number of records matching the key range in query.
53+count
54+ :: forall index
55+ . (IDBIndex index)
56+ => index
57+ -> Maybe KeyRange
58+ -> Aff Int
59+count index range =
60+ fromEffectFnAff $ Fn.runFn2 _count index (toNullable range)
61+
62+-- | Retrieves the value of the first record matching the given key range in query.
63+-- |
64+-- | NOTE
65+-- | The coercion from `a` to any type is unsafe and might throw a runtime error if incorrect.
66+get
67+ :: forall a index
68+ . (IDBIndex index)
69+ => index
70+ -> KeyRange
71+ -> Aff (Maybe a)
72+get index range =
73+ map (toMaybe >>> map unsafeFromForeign) $ fromEffectFnAff $ Fn.runFn2 _get index range
74+
75+-- | Retrieves the keys of records matching the given key range in query
76+-- | (up to the number given if given).
77+getAllKeys
78+ :: forall index
79+ . (IDBIndex index)
80+ => index
81+ -> Maybe KeyRange
82+ -> Maybe Int
83+ -> Aff (Array Key)
84+getAllKeys index range n =
85+ map (map toKey) $ fromEffectFnAff $ Fn.runFn3 _getAllKeys index (toNullable range) (toNullable n)
86+
87+-- | Retrieves the key of the first record matching the given key or key range in query.
88+getKey
89+ :: forall index
90+ . (IDBIndex index)
91+ => index
92+ -> KeyRange
93+ -> Aff (Maybe Key)
94+getKey index range =
95+ map (toMaybe >>> map toKey) $ fromEffectFnAff $ Fn.runFn2 _getKey index range
96+
97+-- | Opens a ValueCursor over the records matching query, ordered by direction.
98+-- | If query is `Nothing`, all records in index are matched.
99+openCursor
100+ :: forall index
101+ . (IDBIndex index)
102+ => index
103+ -> Maybe KeyRange
104+ -> CursorDirection
105+ -> Callbacks ValueCursor
106+ -> Aff Unit
107+openCursor index range dir cb =
108+ fromEffectFnAff $ Fn.runFn4 _openCursor index (toNullable range) (show dir) cb
109+
110+-- | Opens a KeyCursor over the records matching query, ordered by direction.
111+-- | If query is `Nothing`, all records in index are matched.
112+openKeyCursor
113+ :: forall index
114+ . (IDBIndex index)
115+ => index
116+ -> Maybe KeyRange
117+ -> CursorDirection
118+ -> Callbacks KeyCursor
119+ -> Aff Unit
120+openKeyCursor index range dir cb =
121+ fromEffectFnAff $ Fn.runFn4 _openKeyCursor index (toNullable range) (show dir) cb
122+
123+--------------------
124+-- ATTRIBUTES
125+--
126+-- | Returns the key path of the index.
127+keyPath
128+ :: Index
129+ -> KeyPath
130+keyPath =
131+ _keyPath
132+
133+-- | Returns true if the index's multiEntry flag is set.
134+multiEntry
135+ :: Index
136+ -> Boolean
137+multiEntry =
138+ _multiEntry
139+
140+-- | Returns the name of the index.
141+name
142+ :: Index
143+ -> String
144+name =
145+ _name
146+
147+-- | Returns the IDBObjectStore the index belongs to.
148+objectStore
149+ :: Index
150+ -> ObjectStore
151+objectStore =
152+ _objectStore
153+
154+-- | Returns true if the index's unique flag is set.
155+unique
156+ :: Index
157+ -> Boolean
158+unique =
159+ _unique
160+
161+--------------------
162+-- FFI
163+--
164+foreign import _keyPath
165+ :: Index
166+ -> Array String
167+
168+foreign import _multiEntry
169+ :: Index
170+ -> Boolean
171+
172+foreign import _name
173+ :: Index
174+ -> String
175+
176+foreign import _objectStore
177+ :: Index
178+ -> ObjectStore
179+
180+foreign import _unique
181+ :: Index
182+ -> Boolean
183+
184+foreign import _count
185+ :: forall index
186+ . Fn2 index (Nullable KeyRange) (EffectFnAff Int)
187+
188+foreign import _get
189+ :: forall index
190+ . Fn2 index KeyRange (EffectFnAff (Nullable Foreign))
191+
192+foreign import _getAllKeys
193+ :: forall index
194+ . Fn3 index (Nullable KeyRange) (Nullable Int) (EffectFnAff (Array Foreign))
195+
196+foreign import _getKey
197+ :: forall index
198+ . Fn2 index KeyRange (EffectFnAff (Nullable Foreign))
199+
200+foreign import _openCursor
201+ :: forall index
202+ . Fn4 index (Nullable KeyRange) String (Callbacks ValueCursor) (EffectFnAff Unit)
203+
204+foreign import _openKeyCursor
205+ :: forall index
206+ . Fn4 index (Nullable KeyRange) String (Callbacks KeyCursor) (EffectFnAff Unit)
src/Database/IndexedDB/IDBIndex/Internal.jsdeleted−109
@@ -1,109 +0,0 @@
1−const errorHandler = function errorHandler(cb) {
2− return function _handler(e) {
3− cb(new Error(e.target.error.name));
4− };
5−};
6−
7−const successHandler = function successHandler(cb) {
8− return function _handler(e) {
9− cb(e.target.result);
10− };
11−};
12−
13−
14−exports._keyPath = function _keyPath(index) {
15− const path = index.keyPath;
16−
17− if (Array.isArray(path)) {
18− return path;
19− }
20−
21− if (typeof path === 'string' && path !== '') {
22− return [path];
23− }
24−
25− return [];
26−};
27−
28−exports._multiEntry = function _multiEntry(index) {
29− return index.multiEntry;
30−};
31−
32−exports._name = function _name(index) {
33− return index.name;
34−};
35−
36−exports._objectStore = function _objectStore(index) {
37− return index.obectStore;
38−};
39−
40−exports._unique = function _unique(index) {
41− return index.unique;
42−};
43−
44−exports._count = function _count(index, query) {
45− return function aff(success, error) {
46− try {
47− const request = index.count(query);
48− request.onsuccess = successHandler(success);
49− request.onerror = errorHandler(error);
50− } catch (e) {
51− error(new Error(e.name));
52− }
53− };
54−};
55−
56−exports._get = function _get(index, range) {
57− return function aff(success, error) {
58− const request = index.get(range);
59− request.onsuccess = successHandler(success);
60− request.onerror = errorHandler(error);
61− };
62−};
63−
64−/*
65− * NOTE: Require some additional work. The array (which isn't necessarily a list of
66− * polymorphic types in js) can't be easily translated to a PureScript array.
67− *
68− * However, it may be doable to convert the result to some key / value structure with values of
69− * different types.
70−exports._getAll = function _getAll(index, query, count) {
71− return function aff(success, error) {
72− const request = index.getAll(query, count);
73− request.onsuccess = successHandler(success);
74− request.onerror = errorHandler(error);
75− };
76−};
77−*/
78−
79−exports._getAllKeys = function _getAllKeys(index, range, count) {
80− return function aff(success, error) {
81− const request = index.getAllKeys(range, count || undefined);
82− request.onsuccess = successHandler(success);
83− request.onerror = errorHandler(error);
84− };
85−};
86−
87−exports._getKey = function _getKey(index, range) {
88− return function aff(success, error) {
89− const request = index.getKey(range);
90− request.onsuccess = successHandler(success);
91− request.onerror = errorHandler(error);
92− };
93−};
94−
95−exports._openCursor = function _openCursor(index, query, dir) {
96− return function aff(success, error) {
97− const request = index.openCursor(query, dir);
98− request.onsuccess = successHandler(success);
99− request.onerror = errorHandler(error);
100− };
101−};
102−
103−exports._openKeyCursor = function _openKeyCursor(index, query, dir) {
104− return function aff(success, error) {
105− const request = index.openKeyCursor(query, dir);
106− request.onsuccess = successHandler(success);
107− request.onerror = errorHandler(error);
108− };
109−};
src/Database/IndexedDB/IDBIndex/Internal.pursdeleted−145
@@ -1,145 +0,0 @@
1−module Database.IndexedDB.IDBIndex.Internal where
2−
3−import Prelude
4−
5−import Control.Monad.Aff (Aff)
6−import Data.Foreign (Foreign, unsafeFromForeign)
7−import Data.Function.Uncurried as Fn
8−import Data.Function.Uncurried (Fn2, Fn3)
9−import Data.Maybe (Maybe)
10−import Data.Nullable (Nullable, toMaybe, toNullable)
11−
12−import Database.IndexedDB.Core (IDB, CursorDirection, Index, Key, KeyCursor, KeyRange,
13− KeyPath, ObjectStore, ValueCursor)
14−
15−
16−--------------------
17−-- INTERFACES
18−--
19−class IDBIndex index where
20− count :: forall e. index -> Maybe KeyRange -> Aff (idb :: IDB | e) Int
21− get :: forall a e. index -> KeyRange -> Aff (idb :: IDB | e) (Maybe a)
22− getAllKeys :: forall e. index -> Maybe KeyRange -> Maybe Int -> Aff (idb :: IDB | e) (Array Key)
23− getKey :: forall e. index -> KeyRange -> Aff (idb :: IDB | e) (Maybe Key)
24− openCursor :: forall e. index -> Maybe KeyRange -> CursorDirection -> Aff (idb :: IDB | e) ValueCursor
25− openKeyCursor :: forall e. index -> Maybe KeyRange -> CursorDirection -> Aff (idb :: IDB | e) KeyCursor
26−
27−
28−type IDBIndexParameters =
29− { unique :: Boolean
30− , multiEntry :: Boolean
31− }
32−
33−
34−--------------------
35−-- ATTRIBUTES
36−--
37−keyPath :: Index -> KeyPath
38−keyPath =
39− _keyPath
40−
41−
42−multiEntry :: Index -> Boolean
43−multiEntry =
44− _multiEntry
45−
46−
47−name :: Index -> String
48−name =
49− _name
50−
51−
52−objectStore :: Index -> ObjectStore
53−objectStore =
54− _objectStore
55−
56−
57−unique :: Index -> Boolean
58−unique =
59− _unique
60−
61−
62−--------------------
63−-- INSTANCES
64−--
65−instance idbIndexIndex :: IDBIndex Index where
66− count index range =
67− Fn.runFn2 _count index (toNullable range)
68−
69− get index range =
70− (toMaybe >>> map unsafeFromForeign) <$> Fn.runFn2 _get index range
71−
72− getAllKeys index range count =
73− Fn.runFn3 _getAllKeys index (toNullable range) (toNullable count)
74−
75− getKey index range =
76− toMaybe <$> Fn.runFn2 _getKey index range
77−
78− openCursor index range dir =
79− Fn.runFn3 _openCursor index (toNullable range) (show dir)
80−
81− openKeyCursor index range dir =
82− Fn.runFn3 _openKeyCursor index (toNullable range) (show dir)
83−
84−
85−instance idbIndexObjectStore :: IDBIndex ObjectStore where
86− count store range =
87− Fn.runFn2 _count store (toNullable range)
88−
89− get store range =
90− (toMaybe >>> map unsafeFromForeign) <$> Fn.runFn2 _get store range
91−
92− getAllKeys store range count =
93− Fn.runFn3 _getAllKeys store (toNullable range) (toNullable count)
94−
95− getKey store range =
96− toMaybe <$> Fn.runFn2 _getKey store range
97−
98− openCursor store range dir =
99− Fn.runFn3 _openCursor store (toNullable range) (show dir)
100−
101− openKeyCursor store range dir =
102− Fn.runFn3 _openKeyCursor store (toNullable range) (show dir)
103−
104−
105−defaultParameters :: IDBIndexParameters
106−defaultParameters =
107− { unique : false
108− , multiEntry : false
109− }
110−
111−
112−--------------------
113−-- FFI
114−--
115−foreign import _keyPath :: Index -> Array String
116−
117−
118−foreign import _multiEntry :: Index -> Boolean
119−
120−
121−foreign import _name :: Index -> String
122−
123−
124−foreign import _objectStore :: Index -> ObjectStore
125−
126−
127−foreign import _unique :: Index -> Boolean
128−
129−
130−foreign import _count :: forall index e. Fn2 index (Nullable KeyRange) (Aff (idb :: IDB | e) Int)
131−
132−
133−foreign import _get :: forall index e. Fn2 index KeyRange (Aff (idb :: IDB | e) (Nullable Foreign))
134−
135−
136−foreign import _getAllKeys :: forall index e. Fn3 index (Nullable KeyRange) (Nullable Int) (Aff (idb :: IDB | e) (Array Key))
137−
138−
139−foreign import _getKey :: forall index e. Fn2 index KeyRange (Aff (idb :: IDB | e) (Nullable Key))
140−
141−
142−foreign import _openCursor :: forall index e. Fn3 index (Nullable KeyRange) String (Aff (idb :: IDB | e) ValueCursor)
143−
144−
145−foreign import _openKeyCursor :: forall index e. Fn3 index (Nullable KeyRange) String (Aff (idb :: IDB | e) KeyCursor)
src/Database/IndexedDB/IDBKey.purs+11−1
@@ -1,5 +1,15 @@
1+-- | A key has an associated type which is one of: number, date, string, or array.
2+-- |
3+-- | NOTE: Binary keys aren't supported yet.
1module Database.IndexedDB.IDBKey
4module Database.IndexedDB.IDBKey
2 ( module Database.IndexedDB.IDBKey.Internal
5 ( module Database.IndexedDB.IDBKey.Internal
3 ) where
6 ) where
4
7
5−import Database.IndexedDB.IDBKey.Internal(Key, class IDBKey, toKey, fromKey, unsafeFromKey)
8+import Database.IndexedDB.IDBKey.Internal
9+ ( class IDBKey
10+ , Key
11+ , none
12+ , toKey
13+ , fromKey
14+ , unsafeFromKey
15+ )
src/Database/IndexedDB/IDBKey/Internal.js+3−3
@@ -1,8 +1,8 @@
1−exports._dateTimeToForeign = function _dateTimeToForeign(y, m, d, h, mi, s, ms) {
1+export function _dateTimeToForeign(y, m, d, h, mi, s, ms) {
2 return new Date(y, m, d, h, mi, s, ms);
2 return new Date(y, m, d, h, mi, s, ms);
3};
3};
4
4
5−exports._readDateTime = function _readDateTime(parse, right, left, date) {
5+export function _readDateTime(parse, right, left, date) {
6 if (Object.getPrototypeOf(date) !== Date.prototype) {
6 if (Object.getPrototypeOf(date) !== Date.prototype) {
7 return left(typeof date);
7 return left(typeof date);
8 }
8 }
@@ -24,7 +24,7 @@ exports._readDateTime = function _readDateTime(parse, right, left, date) {
24 return right(mdate);
24 return right(mdate);
25};
25};
26
26
27−exports._unsafeReadDateTime = function _unsafeReadDateTime(parse, date) {
27+export function _unsafeReadDateTime(parse, date) {
28 const y = date.getFullYear();
28 const y = date.getFullYear();
29 const m = date.getMonth() + 1;
29 const m = date.getMonth() + 1;
30 const d = date.getDate();
30 const d = date.getDate();
src/Database/IndexedDB/IDBKey/Internal.purs+114−93
@@ -1,119 +1,129 @@
1+-- | A key has an associated type which is one of: number, date, string, or array.
2+-- |
3+-- | NOTE: Binary keys aren't supported yet.
1module Database.IndexedDB.IDBKey.Internal
4module Database.IndexedDB.IDBKey.Internal
2− ( Key(..)
5+ ( Key
6+ , class IDBKey
7+ , toKey
8+ , fromKey
3− , class IDBKey, toKey , fromKey , unsafeFromKey
9+ , unsafeFromKey
4− , extractForeign
10+ , none
5− ) where
11+ ) where
6
12
7import Prelude
13import Prelude
8
14
9−import Control.Alt ((<|>))
15+import Control.Alt ((<|>))
10−import Control.Monad.Except (ExceptT(..), runExceptT)
16+import Control.Monad.Except (ExceptT(..), runExceptT)
11−import Data.Date as Date
17+import Data.Date as Date
12−import Data.DateTime (DateTime(..), Time(..))
18+import Data.DateTime (DateTime(..), Time(..))
19+import Data.Either (Either(..), either, isRight)
13−import Data.Enum (fromEnum, toEnum)
20+import Data.Enum (fromEnum, toEnum)
14−import Data.Foreign as Foreign
21+import Foreign as Foreign
15−import Data.Foreign (Foreign, F)
22+import Foreign (Foreign, F)
16−import Data.Function.Uncurried as Fn
23+import Data.Function.Uncurried as Fn
17−import Data.Function.Uncurried (Fn2, Fn4, Fn7)
24+import Data.Function.Uncurried (Fn2, Fn4, Fn7)
25+import Data.Identity (Identity(..))
18−import Data.List.NonEmpty (NonEmptyList(..))
26+import Data.List.NonEmpty (NonEmptyList(..))
19−import Data.List.Types (List(..))
27+import Data.List.Types (List(..))
20−import Data.NonEmpty (NonEmpty(..))
28+import Data.Maybe (Maybe(..))
21−import Data.Either (Either(..), either, isRight)
29+import Data.NonEmpty (NonEmpty(..))
22−import Data.Identity (Identity(..))
23−import Data.Nullable (Nullable, toNullable)
30+import Data.Nullable (Nullable, toNullable)
24−import Data.Time as Time
31+import Data.Time as Time
25−import Data.Traversable (traverse)
32+import Data.Traversable (traverse)
26−
27
33
28newtype Key = Key Foreign
34newtype Key = Key Foreign
29
35
30−
31−extractForeign :: Key -> Foreign
32−extractForeign (Key f) =
33− f
34−
35−
36--------------------
36--------------------
37-- INTERFACES
37-- INTERFACES
38--
38--
39+-- | Interface describing a key. Use the `unsafeFromKey` to convert a key
40+-- | to a known type (e.g if you only strings as keys, or perfectly knows the
41+-- | type of a given key).
39class IDBKey a where
42class IDBKey a where
40− toKey :: a -> Key
43+ toKey :: a -> Key
41− fromKey :: Key -> F a
44+ fromKey :: Key -> F a
42 unsafeFromKey :: Key -> a
45 unsafeFromKey :: Key -> a
43
46
47+none :: Maybe Key
48+none =
49+ Nothing
44
50
45--------------------
51--------------------
46-- INSTANCES
52-- INSTANCES
47--
53--
48instance eqKey :: Eq Key where
54instance eqKey :: Eq Key where
49 eq a b = (runExceptT >>> runIdentity >>> isRight) $
55 eq a b = (runExceptT >>> runIdentity >>> isRight) $
50− eq <$> ((fromKey a) :: F Int) <*> fromKey b
56+ eq <$> ((fromKey a) :: F Int) <*> fromKey b
51− <|>
57+ <|>
52− eq <$> ((fromKey a) :: F Number) <*> fromKey b
58+ eq <$> ((fromKey a) :: F Number) <*> fromKey b
53− <|>
59+ <|>
54− eq <$> ((fromKey a) :: F String) <*> fromKey b
60+ eq <$> ((fromKey a) :: F String) <*> fromKey b
55− <|>
61+ <|>
56− eq <$> ((fromKey a) :: F DateTime) <*> fromKey b
62+ eq <$> ((fromKey a) :: F DateTime) <*> fromKey b
63+ <|>
64+ eq <$> ((fromKey a) :: F (Array Key)) <*> fromKey b
57 where
65 where
58− runIdentity :: forall a. Identity a -> a
66+ runIdentity :: forall a. Identity a -> a
59− runIdentity (Identity x) = x
67+ runIdentity (Identity x) = x
60−
61
68
62instance ordKey :: Ord Key where
69instance ordKey :: Ord Key where
63− compare a b = (runExceptT >>> runIdentity >>> either (const LT) id) $
70+ compare a b = (runExceptT >>> runIdentity >>> either (const LT) identity) $
64− compare <$> ((fromKey a) :: F Int) <*> fromKey b
71+ compare <$> ((fromKey a) :: F Int) <*> fromKey b
65− <|>
72+ <|>
66− compare <$> ((fromKey a) :: F Number) <*> fromKey b
73+ compare <$> ((fromKey a) :: F Number) <*> fromKey b
67− <|>
74+ <|>
68− compare <$> ((fromKey a) :: F String) <*> fromKey b
75+ compare <$> ((fromKey a) :: F String) <*> fromKey b
69− <|>
76+ <|>
70− compare <$> ((fromKey a) :: F DateTime) <*> fromKey b
77+ compare <$> ((fromKey a) :: F DateTime) <*> fromKey b
78+ <|>
79+ compare <$> ((fromKey a) :: F (Array Key)) <*> fromKey b
71 where
80 where
72− runIdentity :: forall a. Identity a -> a
81+ runIdentity :: forall a. Identity a -> a
73− runIdentity (Identity x) = x
82+ runIdentity (Identity x) = x
74−
75
83
76instance showKey :: Show Key where
84instance showKey :: Show Key where
77 show a = (runExceptT >>> format) $
85 show a = (runExceptT >>> format) $
78− (show <$> (fromKey a :: F Int))
86+ (show <$> (fromKey a :: F Int))
79− <|>
87+ <|>
80− (show <$> (fromKey a :: F Number))
88+ (show <$> (fromKey a :: F Number))
81− <|>
89+ <|>
82− (show <$> (fromKey a :: F String))
90+ (show <$> (fromKey a :: F String))
83− <|>
91+ <|>
84− (show <$> (fromKey a :: F DateTime))
92+ (show <$> (fromKey a :: F DateTime))
93+ <|>
94+ (show <$> (fromKey a :: F (Array Key)))
85 where
95 where
86− format :: forall a. Identity (Either a String) -> String
96+ format :: forall a. Identity (Either a String) -> String
87− format (Identity x) =
97+ format (Identity x) =
88− either (const "(Key)") (\s -> "(Key " <> s <> ")") x
98+ either (const "(Key)") (\s -> "(Key " <> s <> ")") x
89−
90
99
91instance idbKeyKey :: IDBKey Key where
100instance idbKeyKey :: IDBKey Key where
92− toKey = id
101+ toKey = identity
93− fromKey = pure
102+ fromKey = pure
94− unsafeFromKey = id
103+ unsafeFromKey = identity
95
104
105+instance idbKeyForeign :: IDBKey Foreign where
106+ toKey = Key
107+ fromKey (Key f) = pure f
108+ unsafeFromKey (Key f) = f
96
109
97instance idbKeyInt :: IDBKey Int where
110instance idbKeyInt :: IDBKey Int where
98− toKey = Foreign.toForeign >>> Key
111+ toKey = Foreign.unsafeToForeign >>> Key
99− fromKey (Key f) = Foreign.readInt f
112+ fromKey (Key f) = Foreign.readInt f
100 unsafeFromKey (Key f) = Foreign.unsafeFromForeign f
113 unsafeFromKey (Key f) = Foreign.unsafeFromForeign f
101
114
102−
103instance idbKeyNumber :: IDBKey Number where
115instance idbKeyNumber :: IDBKey Number where
104− toKey = Foreign.toForeign >>> Key
116+ toKey = Foreign.unsafeToForeign >>> Key
105− fromKey (Key f) = Foreign.readNumber f
117+ fromKey (Key f) = Foreign.readNumber f
106 unsafeFromKey (Key f) = Foreign.unsafeFromForeign f
118 unsafeFromKey (Key f) = Foreign.unsafeFromForeign f
107
119
108−
109instance idbKeyString :: IDBKey String where
120instance idbKeyString :: IDBKey String where
110− toKey = Foreign.toForeign >>> Key
121+ toKey = Foreign.unsafeToForeign >>> Key
111− fromKey (Key f) = Foreign.readString f
122+ fromKey (Key f) = Foreign.readString f
112 unsafeFromKey (Key f) = Foreign.unsafeFromForeign f
123 unsafeFromKey (Key f) = Foreign.unsafeFromForeign f
113
124
114−
115instance idbKeyDate :: IDBKey DateTime where
125instance idbKeyDate :: IDBKey DateTime where
116− toKey (DateTime d t) = Key $ Fn.runFn7 _dateTimeToForeign
126+ toKey (DateTime d t) = Key $ Fn.runFn7 _dateTimeToForeign
117 (fromEnum $ Date.year d)
127 (fromEnum $ Date.year d)
118 (fromEnum $ Date.month d)
128 (fromEnum $ Date.month d)
119 (fromEnum $ Date.day d)
129 (fromEnum $ Date.day d)
@@ -121,40 +131,51 @@ instance idbKeyDate :: IDBKey DateTime where
121 (fromEnum $ Time.minute t)
131 (fromEnum $ Time.minute t)
122 (fromEnum $ Time.second t)
132 (fromEnum $ Time.second t)
123 (fromEnum $ Time.millisecond t)
133 (fromEnum $ Time.millisecond t)
124− fromKey (Key f) = Fn.runFn4 _readDateTime dateTime dateTimeF dateTimeE f
134+ fromKey (Key f) = Fn.runFn4 _readDateTime dateTime dateTimeF dateTimeE f
125 unsafeFromKey (Key f) = Fn.runFn2 _unsafeReadDateTime dateTime f
135 unsafeFromKey (Key f) = Fn.runFn2 _unsafeReadDateTime dateTime f
126
136
127−
128instance idbKeyArray :: IDBKey a => IDBKey (Array a) where
137instance idbKeyArray :: IDBKey a => IDBKey (Array a) where
129− toKey = Foreign.toForeign >>> Key
138+ toKey = Foreign.unsafeToForeign >>> Key
130− fromKey (Key f) = Foreign.readArray f >>= traverse (Key >>> fromKey)
139+ fromKey (Key f) = Foreign.readArray f >>= traverse (Key >>> fromKey)
131 unsafeFromKey (Key f) = map unsafeFromKey (Foreign.unsafeFromForeign f)
140 unsafeFromKey (Key f) = map unsafeFromKey (Foreign.unsafeFromForeign f)
132
141
133−
142+-- FFI constructor to build a DateTime from years, months, days, hours, minutes, seconds and millis
143+dateTime
144+ :: Int -- ^ years
145+ -> Int -- ^ months
146+ -> Int -- ^ days
147+ -> Int -- ^ hours
148+ -> Int -- ^ minutes
149+ -> Int -- ^ seconds
150+ -> Int -- ^ milliseconds
134−dateTime :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> Nullable DateTime
151+ -> Nullable DateTime
135dateTime y m d h mi s ms =
152dateTime y m d h mi s ms =
136 toNullable $ DateTime
153 toNullable $ DateTime
137− <$> (Date.canonicalDate <$> toEnum y <*> toEnum m <*> toEnum d)
154+ <$> (Date.canonicalDate <$> toEnum y <*> toEnum m <*> toEnum d)
138− <*> (Time <$> toEnum h <*> toEnum mi <*> toEnum s <*> toEnum ms)
155+ <*> (Time <$> toEnum h <*> toEnum mi <*> toEnum s <*> toEnum ms)
139−
140
156
157+-- FFI constructor to convert a JS `Date` into a successful `F DateTime`
141−dateTimeF :: DateTime -> F DateTime
158+dateTimeF
159+ :: DateTime
160+ -> F DateTime
142dateTimeF =
161dateTimeF =
143 Right >>> Identity >>> ExceptT
162 Right >>> Identity >>> ExceptT
144
163
145−
164+-- FFI constructor to convert a string into an errored `F DateTime`
146−dateTimeE :: String -> F DateTime
165+dateTimeE
166+ :: String
167+ -> F DateTime
147dateTimeE =
168dateTimeE =
148 Foreign.TypeMismatch "Date" >>> flip NonEmpty Nil >>> NonEmptyList >>> Left >>> Identity >>> ExceptT
169 Foreign.TypeMismatch "Date" >>> flip NonEmpty Nil >>> NonEmptyList >>> Left >>> Identity >>> ExceptT
149
170
150−
151--------------------
171--------------------
152-- FFI
172-- FFI
153--
173--
154−foreign import _dateTimeToForeign :: Fn7 Int Int Int Int Int Int Int Foreign
174+foreign import _dateTimeToForeign
155−
175+ :: Fn7 Int Int Int Int Int Int Int Foreign
156−
157−foreign import _readDateTime :: Fn4 (Int -> Int -> Int -> Int -> Int -> Int -> Int -> Nullable DateTime) (DateTime -> F DateTime) (String -> F DateTime) Foreign (F DateTime)
158
176
177+foreign import _readDateTime
178+ :: Fn4 (Int -> Int -> Int -> Int -> Int -> Int -> Int -> Nullable DateTime) (DateTime -> F DateTime) (String -> F DateTime) Foreign (F DateTime)
159
179
180+foreign import _unsafeReadDateTime
160−foreign import _unsafeReadDateTime :: Fn2 (Int -> Int -> Int -> Int -> Int -> Int -> Int -> Nullable DateTime) Foreign DateTime
181+ :: Fn2 (Int -> Int -> Int -> Int -> Int -> Int -> Int -> Nullable DateTime) Foreign DateTime
src/Database/IndexedDB/IDBKeyRange.js+9−9
@@ -1,4 +1,4 @@
1−exports._bound = function _bound(lower, upper, lowerOpen, upperOpen) {
1+export function _bound(lower, upper, lowerOpen, upperOpen) {
2 try {
2 try {
3 return IDBKeyRange.bound(lower, upper, lowerOpen, upperOpen);
3 return IDBKeyRange.bound(lower, upper, lowerOpen, upperOpen);
4 } catch (e) {
4 } catch (e) {
@@ -6,34 +6,34 @@ exports._bound = function _bound(lower, upper, lowerOpen, upperOpen) {
6 }
6 }
7};
7};
8
8
9−exports._includes = function _includes(range, key) {
9+export function _includes(range, key) {
10 return range.includes(key);
10 return range.includes(key);
11};
11};
12
12
13−exports._lower = function _lower(range) {
13+export function _lower(range) {
14 return range.lower;
14 return range.lower;
15};
15};
16
16
17−exports._lowerBound = function _lowerBound(lower, open) {
17+export function _lowerBound(lower, open) {
18 return IDBKeyRange.lowerBound(lower, open);
18 return IDBKeyRange.lowerBound(lower, open);
19};
19};
20
20
21−exports._lowerOpen = function _lowerOpen(range) {
21+export function _lowerOpen(range) {
22 return range.lowerOpen;
22 return range.lowerOpen;
23};
23};
24
24
25−exports._only = function _only(key) {
25+export function _only(key) {
26 return IDBKeyRange.only(key);
26 return IDBKeyRange.only(key);
27};
27};
28
28
29−exports._upper = function _upper(range) {
29+export function _upper(range) {
30 return range.upper;
30 return range.upper;
31};
31};
32
32
33−exports._upperBound = function _upperBound(upper, open) {
33+export function _upperBound(upper, open) {
34 return IDBKeyRange.upperBound(upper, open);
34 return IDBKeyRange.upperBound(upper, open);
35};
35};
36
36
37−exports._upperOpen = function _upperOpen(range) {
37+export function _upperOpen(range) {
38 return range.upperOpen;
38 return range.upperOpen;
39};
39};
src/Database/IndexedDB/IDBKeyRange.purs+120−60
@@ -1,110 +1,170 @@
1+-- | A key has an associated type which is one of: number, date, string, binary, or array.
1module Database.IndexedDB.IDBKeyRange
2module Database.IndexedDB.IDBKeyRange
2− ( class IDBKeyRange, includes
3+ -- * Types
4+ ( Open
5+
6+ -- * Constructors
3 , only
7 , only
4 , lowerBound
8 , lowerBound
5 , upperBound
9 , upperBound
6 , bound
10 , bound
11+
12+ -- * Interface
13+ , includes
14+
15+ -- * Attributes
7 , lower
16 , lower
8 , upper
17 , upper
9 , lowerOpen
18 , lowerOpen
10 , upperOpen
19 , upperOpen
11 ) where
20 ) where
12
21
13−import Prelude
22+import Prelude (($), (>>>), map)
14−
15−import Data.Foreign (Foreign)
16−import Data.Function.Uncurried as Fn
17−import Data.Function.Uncurried (Fn2, Fn4)
18−import Data.Maybe (Maybe)
19−import Data.Nullable (Nullable, toMaybe)
20
23
21−import Database.IndexedDB.Core (KeyRange)
24+import Foreign (Foreign)
22−import Database.IndexedDB.IDBKey.Internal (class IDBKey, Key(..), toKey, extractForeign)
25+import Data.Function.Uncurried as Fn
26+import Data.Function.Uncurried (Fn2, Fn4)
27+import Data.Maybe (Maybe)
28+import Data.Nullable (Nullable, toMaybe)
23
29
30+import Database.IndexedDB.Core (class IDBKeyRange, KeyRange)
31+import Database.IndexedDB.IDBKey.Internal (class IDBKey, Key, toKey, unsafeFromKey)
24
32
25--------------------
33--------------------
26−-- INTERFACES
34+-- TYPES
27--
35--
28−class IDBKeyRange range where
29− includes :: range -> Key -> Boolean
30
36
37+-- | Type alias for open
38+type Open = Boolean
31
39
32−only :: forall a. (IDBKey a) => a -> KeyRange
40+--------------------
33−only key =
41+-- CONSTRUCTORS
34− _only (extractForeign $ toKey key)
42+--
35−
36
43
44+-- | Returns a new IDBKeyRange spanning only key.
45+only
46+ :: forall a
47+ . (IDBKey a)
48+ => a
49+ -> KeyRange
50+only key =
51+ _only (unsafeFromKey $ toKey key)
52+
53+-- | Returns a new IDBKeyRange starting at key with no upper bound.
54+-- | If `Open` is `true`, key is not included in the range.
37−lowerBound :: forall a. (IDBKey a) => a -> Boolean -> KeyRange
55+lowerBound
56+ :: forall a
57+ . (IDBKey a)
58+ => a
59+ -> Open
60+ -> KeyRange
38lowerBound key open =
61lowerBound key open =
39− Fn.runFn2 _lowerBound (extractForeign $ toKey key) open
62+ Fn.runFn2 _lowerBound (unsafeFromKey $ toKey key) open
40−
63+
41−
64+-- | Returns a new IDBKeyRange with no lower bound and ending at key.
65+-- | If `Open` is `true`, key is not included in the range.
42−upperBound :: forall a. (IDBKey a) => a -> Boolean -> KeyRange
66+upperBound
67+ :: forall a
68+ . (IDBKey a)
69+ => a
70+ -> Open
71+ -> KeyRange
43upperBound key open =
72upperBound key open =
44− Fn.runFn2 _upperBound (extractForeign $ toKey key) open
73+ Fn.runFn2 _upperBound (unsafeFromKey $ toKey key) open
45−
74+
75+-- | Returns a new IDBKeyRange spanning from `lower` to `upper`.
76+-- | If `lowerOpen` is `true`, `lower` is not included in the range.
77+-- | If `upperOpen` is `true`, `upper` is not included in the range.
78+-- |
79+-- | It throws a `DataError` if the bound is invalid.
80+bound
81+ :: forall key
82+ . (IDBKey key)
46−bound :: forall a. (IDBKey a) => { lower :: a, upper :: a, lowerOpen :: Boolean, upperOpen :: Boolean } -> Maybe KeyRange
83+ => { lower :: key, upper :: key, lowerOpen :: Boolean, upperOpen :: Boolean }
84+ -> Maybe KeyRange
47bound { lower: key1, upper: key2, lowerOpen: open1, upperOpen: open2 } =
85bound { lower: key1, upper: key2, lowerOpen: open1, upperOpen: open2 } =
48 toMaybe
86 toMaybe
49− $ Fn.runFn4 _bound (extractForeign $ toKey key1) (extractForeign $ toKey key2) open1 open2
87+ $ Fn.runFn4 _bound (unsafeFromKey $ toKey key1) (unsafeFromKey $ toKey key2) open1 open2
88+
89+--------------------
90+-- INTERFACE
91+--
50
92
93+-- | Returns true if key is included in the range, and false otherwise.
94+includes
95+ :: forall key range
96+ . (IDBKey key)
97+ => (IDBKeyRange range)
98+ => range
99+ -> key
100+ -> Boolean
101+includes range =
102+ toKey >>> unsafeFromKey >>> Fn.runFn2 _includes range
51
103
52--------------------
104--------------------
53-- ATTRIBUTES
105-- ATTRIBUTES
54--
106--
107+-- | Returns lower bound if any.
108+lower
55−lower :: KeyRange -> Maybe Key
109+ :: KeyRange
110+ -> Maybe Key
56lower =
111lower =
57− _lower >>> toMaybe >>> map Key
112+ _lower >>> toMaybe >>> map toKey
58
113
59−
114+-- | Returns upper bound if any.
115+upper
60−upper :: KeyRange -> Maybe Key
116+ :: KeyRange
117+ -> Maybe Key
61upper =
118upper =
62− _upper >>> toMaybe >>> map Key
119+ _upper >>> toMaybe >>> map toKey
63−
64
120
121+-- | Returns true if the lower open flag is set, false otherwise.
65−lowerOpen :: KeyRange -> Boolean
122+lowerOpen
123+ :: KeyRange
124+ -> Boolean
66lowerOpen =
125lowerOpen =
67 _lowerOpen
126 _lowerOpen
68
127
69−
128+-- | Returns true if the upper open flag is set, false otherwise.
70−upperOpen :: KeyRange -> Boolean
129+upperOpen
130+ :: KeyRange
131+ -> Boolean
71upperOpen =
132upperOpen =
72 _upperOpen
133 _upperOpen
73
134
74−
75−--------------------
76−-- INSTANCES
77−--
78−instance idbKeyRangeKeyRange :: IDBKeyRange KeyRange where
79− includes range =
80− extractForeign >>> Fn.runFn2 _includes range
81−
82−
83--------------------
135--------------------
84-- FFI
136-- FFI
85--
137--
86−foreign import _bound :: Fn4 Foreign Foreign Boolean Boolean (Nullable KeyRange)
87−
88−
89−foreign import _includes :: forall range. Fn2 range Foreign Boolean
90−
91−
92−foreign import _lower :: KeyRange -> Nullable Foreign
93−
94−
95−foreign import _lowerBound :: Fn2 Foreign Boolean KeyRange
96−
97
138
98−foreign import _lowerOpen :: KeyRange -> Boolean
139+foreign import _only
140+ :: Foreign
141+ -> KeyRange
99
142
143+foreign import _lowerBound
144+ :: Fn2 Foreign Boolean KeyRange
100
145
146+foreign import _upperBound
101−foreign import _only :: Foreign -> KeyRange
147+ :: Fn2 Foreign Boolean KeyRange
102
148
149+foreign import _bound
150+ :: Fn4 Foreign Foreign Boolean Boolean (Nullable KeyRange)
103
151
104−foreign import _upper :: KeyRange -> Nullable Foreign
152+foreign import _includes
153+ :: forall range
154+ . Fn2 range Foreign Boolean
105
155
156+foreign import _lower
157+ :: KeyRange
158+ -> Nullable Foreign
106
159
107−foreign import _upperBound :: Fn2 Foreign Boolean KeyRange
160+foreign import _upper
161+ :: KeyRange
162+ -> Nullable Foreign
108
163
164+foreign import _lowerOpen
165+ :: KeyRange
166+ -> Boolean
109
167
110−foreign import _upperOpen :: KeyRange -> Boolean
168+foreign import _upperOpen
169+ :: KeyRange
170+ -> Boolean
src/Database/IndexedDB/IDBObjectStore.js+84−38
@@ -1,6 +1,6 @@
1const errorHandler = function errorHandler(cb) {
1const errorHandler = function errorHandler(cb) {
2 return function _handler(e) {
2 return function _handler(e) {
3− cb(new Error(e.target.error.name));
3+ cb(e.target.error);
4 };
4 };
5};
5};
6
6
@@ -15,28 +15,44 @@ const toArray = function toArray(xs) {
15};
15};
16
16
17
17
18−exports._add = function _add(store, value, key) {
18+export function _add(store, value, key) {
19− return function aff(success, error) {
19+ return function aff(error, success) {
20+ try {
20− const request = store.add(value, key || undefined);
21+ const request = store.add(value, key || undefined);
21− request.onsuccess = successHandler(success);
22+ request.onsuccess = successHandler(success);
22− request.onerror = errorHandler(error);
23+ request.onerror = errorHandler(error);
24+ } catch (e) {
25+ error(e);
26+ }
27+
28+ return function canceler(_, cancelerError) {
29+ cancelerError(new Error("Can't cancel IDB Effects"));
30+ };
23 };
31 };
24};
32};
25
33
26−exports._autoIncrement = function _autoIncrement(store) {
34+export function _autoIncrement(store) {
27 return store.autoIncrement;
35 return store.autoIncrement;
28};
36};
29
37
30−exports._clear = function _clear(store) {
38+export function _clear(store) {
31− return function aff(success, error) {
39+ return function aff(error, success) {
40+ try {
32− const request = store.clear();
41+ const request = store.clear();
33− request.onsuccess = successHandler(success);
42+ request.onsuccess = successHandler(success);
34− request.onerror = errorHandler(error);
43+ request.onerror = errorHandler(error);
44+ } catch (e) {
45+ error(e);
46+ }
47+
48+ return function canceler(_, cancelerError) {
49+ cancelerError(new Error("Can't cancel IDB Effects"));
50+ };
35 };
51 };
36};
52};
37
53
38−exports._createIndex = function _createIndex(store, name, path, params) {
54+export function _createIndex(store, name, path, params) {
39− return function eff() {
55+ return function aff(error, success) {
40 var keyPath;
56 var keyPath;
41
57
42 try {
58 try {
@@ -53,46 +69,69 @@ exports._createIndex = function _createIndex(store, name, path, params) {
53 keyPath = path;
69 keyPath = path;
54 }
70 }
55
71
56− return store.createIndex(name, keyPath, params);
72+ const index = store.createIndex(name, keyPath, params);
73+ success(index);
57 } catch (e) {
74 } catch (e) {
58− throw new Error(e.name);
75+ error(e);
59 }
76 }
77+
78+ return function canceler(_, cancelerError) {
79+ cancelerError(new Error("Can't cancel IDB Effects"));
80+ };
60 };
81 };
61};
82};
62
83
63−exports._deleteIndex = function _deleteIndex(store, name) {
84+export function _deleteIndex(store, name) {
64− return function eff() {
85+ return function aff(error, success) {
65 try {
86 try {
66 store.deleteIndex(name);
87 store.deleteIndex(name);
88+ success();
67 } catch (e) {
89 } catch (e) {
68− throw new Error(e.name);
90+ error(e);
69 }
91 }
92+
93+ return function canceler(_, cancelerError) {
94+ cancelerError(new Error("Can't cancel IDB Effects"));
95+ };
70 };
96 };
71};
97};
72
98
73−exports._delete = function _delete(store, query) {
99+export function _delete(store, query) {
74− return function aff(success, error) {
100+ return function aff(error, success) {
101+ try {
75− const request = store.delete(query);
102+ const request = store.delete(query);
76− request.onsuccess = successHandler(success);
103+ request.onsuccess = successHandler(success);
77− request.onerror = errorHandler(error);
104+ request.onerror = errorHandler(error);
105+ } catch (e) {
106+ error(e);
107+ }
108+
109+ return function canceler(_, cancelerError) {
110+ cancelerError(new Error("Can't cancel IDB Effects"));
111+ };
78 };
112 };
79};
113};
80
114
81−exports._index = function _index(store, name) {
115+export function _index(store, name) {
82− return function eff() {
116+ return function aff(error, success) {
83 try {
117 try {
84− return store.index(name);
118+ const index = store.index(name);
119+ success(index);
85 } catch (e) {
120 } catch (e) {
86− throw new Error(e.name);
121+ error(e);
87 }
122 }
123+
124+ return function canceler(_, cancelerError) {
125+ cancelerError(new Error("Can't cancel IDB Effects"));
126+ };
88 };
127 };
89};
128};
90
129
91−exports._indexNames = function _indexNames(store) {
130+export function _indexNames(store) {
92 return toArray(store.indexNames);
131 return toArray(store.indexNames);
93};
132};
94
133
95−exports._keyPath = function _keyPath(store) {
134+export function _keyPath(store) {
96 const path = store.keyPath;
135 const path = store.keyPath;
97
136
98 if (Array.isArray(path)) {
137 if (Array.isArray(path)) {
@@ -106,19 +145,26 @@ exports._keyPath = function _keyPath(store) {
106 return [];
145 return [];
107};
146};
108
147
109−exports._name = function _name(store) {
148+export function _name(store) {
110 return store.name;
149 return store.name;
111};
150};
112
151
113−exports._put = function _put(store, value, key) {
152+export function _put(store, value, key) {
114− return function aff(success, error) {
153+ return function aff(error, success) {
154+ try {
115− const request = store.put(value, key || undefined);
155+ const request = store.put(value, key || undefined);
116− request.onsuccess = successHandler(success);
156+ request.onsuccess = successHandler(success);
117− request.onerror = errorHandler(error);
157+ request.onerror = errorHandler(error);
158+ } catch (e) {
159+ error(e);
160+ }
161+
162+ return function canceler(_, cancelerError) {
163+ cancelerError(new Error("Can't cancel IDB Effects"));
164+ };
118 };
165 };
119};
166};
120
167
121−exports._transaction = function _transaction(store) {
168+export function _transaction(store) {
122 return store.transaction;
169 return store.transaction;
123};
170};
124−
src/Database/IndexedDB/IDBObjectStore.purs+217−109
@@ -1,149 +1,257 @@
1+-- | An object store is the primary storage mechanism for storing data in a database.
1module Database.IndexedDB.IDBObjectStore
2module Database.IndexedDB.IDBObjectStore
2− ( class IDBObjectStore, add, clear, createIndex, delete, deleteIndex, index, put
3+ -- * Types
3− , module Database.IndexedDB.IDBIndex.Internal
4+ ( IndexName
4− , IDBObjectStoreParameters
5+ , IndexParameters
5− , IndexName
6+ , defaultParameters
7+
8+ -- * Interface
9+ , add
10+ , clear
11+ , createIndex
12+ , delete
13+ , deleteIndex
14+ , index
15+ , put
16+
17+ -- * Attributes
6 , autoIncrement
18 , autoIncrement
7 , indexNames
19 , indexNames
8 , keyPath
20 , keyPath
9 , name
21 , name
10 , transaction
22 , transaction
11− , defaultParameters
12− ) where
13−
14−import Prelude (Unit, ($), (<$>))
15
23
16−import Control.Monad.Aff (Aff)
24+ -- * Re-Exports
17−import Control.Monad.Eff (Eff)
25+ , module Database.IndexedDB.IDBIndex
18−import Control.Monad.Eff.Exception (EXCEPTION)
26+ ) where
19−import Data.Foreign (Foreign)
20−import Data.Function.Uncurried as Fn
21−import Data.Function.Uncurried (Fn2, Fn3, Fn4)
22−import Data.Maybe (Maybe)
23−import Data.Nullable (Nullable, toNullable)
24
27
25−import Database.IndexedDB.Core (IDB, Index, KeyRange, KeyPath, ObjectStore, Transaction)
28+import Database.IndexedDB.Core
26−import Database.IndexedDB.IDBIndex.Internal (class IDBIndex, IDBIndexParameters, count, get, getAllKeys, getKey, openCursor, openKeyCursor)
27−import Database.IndexedDB.IDBKey.Internal (Key(Key), extractForeign)
28
29
30+import Data.Function.Uncurried (Fn2, Fn3, Fn4)
31+import Data.Function.Uncurried as Fn
32+import Data.Maybe (Maybe)
33+import Data.Nullable (Nullable, toNullable)
34+import Database.IndexedDB.IDBIndex (count, get, getAllKeys, getKey, openCursor, openKeyCursor)
35+import Database.IndexedDB.IDBKey.Internal (class IDBKey, Key, unsafeFromKey, toKey)
36+import Effect.Aff (Aff)
37+import Effect.Aff.Compat (EffectFnAff, fromEffectFnAff)
38+import Foreign (Foreign)
39+import Prelude (Unit, map, ($), (<$>), (>>>), (<<<))
29
40
30--------------------
41--------------------
31−-- INTERFACES
42+-- TYPES
32--
43--
33−class IDBObjectStore store where
34− add :: forall value e. store -> value -> Maybe Key -> Aff (idb :: IDB | e) Key
35− clear :: forall e. store -> Aff (idb :: IDB | e) Unit
36− createIndex :: forall e. store -> IndexName -> KeyPath -> IDBIndexParameters -> Eff (idb :: IDB, exception :: EXCEPTION | e) Index
37− delete :: forall e. store -> KeyRange -> Aff (idb :: IDB | e) Unit
38− deleteIndex :: forall e. store -> IndexName -> Eff (idb :: IDB, exception :: EXCEPTION | e) Unit
39− index :: forall e. store -> IndexName -> Eff (idb :: IDB, exception :: EXCEPTION | e) Index
40− put :: forall value e. store -> value -> Maybe Key -> Aff (idb :: IDB | e) Key
41−
42
44
45+-- | Type alias for IndexName
43type IndexName = String
46type IndexName = String
44
47
48+-- | Flags to set on the index.
49+-- |
50+-- | An index has a `unique` flag. When this flag is set, the index enforces that no
51+-- | two records in the index has the same key. If a record in the index’s referenced
52+-- | object store is attempted to be inserted or modified such that evaluating the index’s
53+-- | key path on the records new value yields a result which already exists in the index,
54+-- | then the attempted modification to the object store fails.
55+-- |
56+-- | An index has a `multiEntry` flag. This flag affects how the index behaves when the
57+-- | result of evaluating the index’s key path yields an array key. If the `multiEntry` flag
58+-- | is unset, then a single record whose key is an array key is added to the index.
59+-- | If the `multiEntry` flag is true, then the one record is added to the index for each
60+-- | of the subkeys.
61+type IndexParameters =
62+ { unique :: Boolean
63+ , multiEntry :: Boolean
64+ }
45
65
46−type IDBObjectStoreParameters =
66+defaultParameters :: IndexParameters
47− { keyPath :: KeyPath
67+defaultParameters =
48− , autoIncrement :: Boolean
68+ { unique: false
69+ , multiEntry: false
49 }
70 }
50
71
72+--------------------
73+-- INTERFACES
74+--
75+
76+-- | Adds or updates a record in store with the given value and key.
77+-- |
78+-- | If the store uses in-line keys and key is specified a "DataError" DOMException
79+-- | will be thrown.
80+-- |
81+-- | If add() is used, and if a record with the key already exists the request will fail,
82+-- | with a "ConstraintError" DOMException.
83+add
84+ :: forall key val store
85+ . (IDBKey key)
86+ => (IDBObjectStore store)
87+ => store
88+ -> val
89+ -> Maybe key
90+ -> Aff Key
91+add store value key =
92+ map toKey $ fromEffectFnAff $ Fn.runFn3 _add store value (toNullable $ (toKey >>> unsafeFromKey) <$> key)
93+
94+-- | Deletes all records in store.
95+clear
96+ :: forall store
97+ . (IDBObjectStore store)
98+ => store
99+ -> Aff Unit
100+clear =
101+ fromEffectFnAff <<< _clear
102+
103+-- | Creates a new index in store with the given name, keyPath and options and
104+-- | returns a new IDBIndex. If the keyPath and options define constraints that
105+-- | cannot be satisfied with the data already in store the upgrade transaction
106+-- | will abort with a "ConstraintError" DOMException.
107+-- |
108+-- | Throws an "InvalidStateError" DOMException if not called within an upgrade transaction.
109+createIndex
110+ :: forall store
111+ . (IDBObjectStore store)
112+ => store
113+ -> IndexName
114+ -> KeyPath
115+ -> IndexParameters
116+ -> Aff Index
117+createIndex store name' path params =
118+ fromEffectFnAff $ Fn.runFn4 _createIndex store name' path params
119+
120+-- | Deletes records in store with the given key or in the given key range in query.
121+delete
122+ :: forall store
123+ . (IDBObjectStore store)
124+ => store
125+ -> KeyRange
126+ -> Aff Unit
127+delete store range =
128+ fromEffectFnAff $ Fn.runFn2 _delete store range
129+
130+-- | Deletes the index in store with the given name.
131+-- |
132+-- | Throws an "InvalidStateError" DOMException if not called within an upgrade transaction.
133+deleteIndex
134+ :: forall store
135+ . (IDBObjectStore store)
136+ => store
137+ -> IndexName
138+ -> Aff Unit
139+deleteIndex store name' =
140+ fromEffectFnAff $ Fn.runFn2 _deleteIndex store name'
141+
142+-- | Returns an IDBIndex for the index named name in store.
143+index
144+ :: forall store
145+ . (IDBObjectStore store)
146+ => store
147+ -> IndexName
148+ -> Aff Index
149+index store name' =
150+ fromEffectFnAff $ Fn.runFn2 _index store name'
151+
152+-- | Adds or updates a record in store with the given value and key.
153+-- |
154+-- | If the store uses in-line keys and key is specified a "DataError" DOMException
155+-- | will be thrown.
156+-- |
157+-- | If put() is used, any existing record with the key will be replaced.
158+put
159+ :: forall val key store
160+ . (IDBKey key)
161+ => (IDBObjectStore store)
162+ => store
163+ -> val
164+ -> Maybe key
165+ -> Aff Key
166+put store value key =
167+ map toKey $ fromEffectFnAff $ Fn.runFn3 _put store value (toNullable $ (toKey >>> unsafeFromKey) <$> key)
51
168
52--------------------
169--------------------
53-- ATTRIBUTES
170-- ATTRIBUTES
54--
171--
172+-- | Returns `true` if the store has a key generator, and `false` otherwise.
55−autoIncrement :: ObjectStore -> Boolean
173+autoIncrement
174+ :: ObjectStore
175+ -> Boolean
56autoIncrement =
176autoIncrement =
57 _autoIncrement
177 _autoIncrement
58
178
59−
179+--| Returns a list of the names of indexes in the store.
180+indexNames
60−indexNames :: ObjectStore -> Array String
181+ :: ObjectStore
182+ -> Array String
61indexNames =
183indexNames =
62 _indexNames
184 _indexNames
63
185
64−
186+-- | Returns the key path of the store, or empty array if none
187+keyPath
65−keyPath :: ObjectStore -> Array String
188+ :: ObjectStore
189+ -> Array String
66keyPath =
190keyPath =
67 _keyPath
191 _keyPath
68
192
69−
193+-- | Returns the name of the store.
194+name
70−name :: ObjectStore -> String
195+ :: ObjectStore
196+ -> String
71name =
197name =
72 _name
198 _name
73
199
74−
200+-- | Returns the associated transaction.
75−transaction :: ObjectStore -> Transaction
201+transaction
202+ :: ObjectStore
203+ -> Transaction
76transaction =
204transaction =
77 _transaction
205 _transaction
78
206
79−
80−--------------------
81−-- INSTANCES
82−--
83−instance idbObjectStoreObjectStore :: IDBObjectStore ObjectStore where
84− add store value key =
85− Key <$> Fn.runFn3 _add store value (toNullable $ extractForeign <$> key)
86−
87− clear =
88− _clear
89−
90− createIndex store name' path params =
91− Fn.runFn4 _createIndex store name' path params
92−
93− delete store range =
94− Fn.runFn2 _delete store range
95−
96− deleteIndex store name' =
97− Fn.runFn2 _deleteIndex store name'
98−
99− index store name' =
100− Fn.runFn2 _index store name'
101−
102− put store value key =
103− Key <$> Fn.runFn3 _put store value (toNullable $ extractForeign <$> key)
104−
105−
106−defaultParameters :: IDBObjectStoreParameters
107−defaultParameters =
108− { keyPath : []
109− , autoIncrement : false
110− }
111−
112−
113--------------------
207--------------------
114-- FFI
208-- FFI
115--
209--
210+foreign import _add
211+ :: forall val store
116−foreign import _add :: forall value e. Fn3 ObjectStore value (Nullable Foreign) (Aff (idb :: IDB | e) Foreign)
212+ . Fn3 store val (Nullable Foreign) (EffectFnAff Foreign)
117−
213+
118−
119−foreign import _autoIncrement :: ObjectStore -> Boolean
214+foreign import _autoIncrement
120−
215+ :: ObjectStore
121−
216+ -> Boolean
217+
122−foreign import _clear :: forall e. ObjectStore -> Aff (idb :: IDB | e) Unit
218+foreign import _clear
123−
219+ :: forall store
124−
220+ . store
221+ -> EffectFnAff Unit
222+
223+foreign import _createIndex
224+ :: forall store
125−foreign import _createIndex :: forall e. Fn4 ObjectStore String (Array String) { unique :: Boolean, multiEntry :: Boolean } (Eff (idb :: IDB | e) Index)
225+ . Fn4 store String (Array String) { unique :: Boolean, multiEntry :: Boolean } (EffectFnAff Index)
126−
226+
127−
128−foreign import _delete :: forall e. Fn2 ObjectStore KeyRange (Aff (idb :: IDB | e) Unit)
227+foreign import _delete
129−
228+ :: forall store
130−
229+ . Fn2 store KeyRange (EffectFnAff Unit)
230+
131−foreign import _deleteIndex :: forall e. Fn2 ObjectStore String (Eff (idb :: IDB | e) Unit)
231+foreign import _deleteIndex
132−
232+ :: forall store
133−
233+ . Fn2 store String (EffectFnAff Unit)
234+
134−foreign import _index :: forall e. Fn2 ObjectStore String (Eff (idb :: IDB | e) Index)
235+foreign import _index
135−
236+ :: forall store
136−
237+ . Fn2 store String (EffectFnAff Index)
238+
137−foreign import _indexNames :: ObjectStore -> Array String
239+foreign import _indexNames
138−
240+ :: ObjectStore
139−
241+ -> Array String
242+
140−foreign import _keyPath :: ObjectStore -> Array String
243+foreign import _keyPath
141−
244+ :: ObjectStore
142−
245+ -> Array String
246+
143−foreign import _name :: ObjectStore -> String
247+foreign import _name
144−
248+ :: ObjectStore
145−
249+ -> String
250+
251+foreign import _put
252+ :: forall val store
146−foreign import _put :: forall value e. Fn3 ObjectStore value (Nullable Foreign) (Aff (idb :: IDB | e) Foreign)
253+ . Fn3 store val (Nullable Foreign) (EffectFnAff Foreign)
147−
254+
148−
149−foreign import _transaction :: ObjectStore -> Transaction
255+foreign import _transaction
256+ :: ObjectStore
257+ -> Transaction
src/Database/IndexedDB/IDBTransaction.js+50−17
@@ -1,20 +1,29 @@
1−exports._abort = function _abort(tx) {
1+export function _abort(tx) {
2− return function eff() {
2+ return function aff(error, success) {
3 try {
3 try {
4 tx.abort();
4 tx.abort();
5+ success();
5 } catch (e) {
6 } catch (e) {
6− throw new Error(e.name);
7+ error(e);
7 }
8 }
9+
10+ return function canceler(_, cancelerError) {
11+ cancelerError(new Error("Can't cancel IDB Effects"));
12+ };
8 };
13 };
9};
14};
10
15
16+export function _db(tx) {
17+ return tx.db;
18+};
19+
11−exports._error = function _error(tx) {
20+export function _error(tx) {
12 return tx.error == null
21 return tx.error == null
13 ? null
22 ? null
14− : new Error(tx.error.name);
23+ : tx.error;
15};
24};
16
25
17−exports._mode = function _mode(ReadOnly, ReadWrite, VersionChange, tx) {
26+export function _mode(ReadOnly, ReadWrite, VersionChange, tx) {
18 if (tx.mode === 'readwrite') {
27 if (tx.mode === 'readwrite') {
19 return ReadWrite;
28 return ReadWrite;
20 } else if (tx.mode === 'versionchange') {
29 } else if (tx.mode === 'versionchange') {
@@ -24,36 +33,60 @@ exports._mode = function _mode(ReadOnly, ReadWrite, VersionChange, tx) {
24 return ReadOnly;
33 return ReadOnly;
25};
34};
26
35
27−exports._objectStore = function _objectStore(tx, name) {
36+export function _objectStore(tx, name) {
28− return function eff() {
37+ return function aff(error, success) {
29 try {
38 try {
30− return tx.objectStore(name);
39+ const store = tx.objectStore(name);
40+ success(store);
31 } catch (e) {
41 } catch (e) {
32− throw new Error(e.name);
42+ error(e);
33 }
43 }
44+
45+ return function canceler(_, cancelerError) {
46+ cancelerError(new Error("Can't cancel IDB Effects"));
47+ };
34 };
48 };
35};
49};
36
50
51+export function _objectStoreNames(tx) {
52+ return tx.objectStoreNames;
53+};
54+
37−exports._onAbort = function _onAbort(tx, f) {
55+export function _onAbort(tx, f) {
38− return function eff() {
56+ return function aff(error, success) {
39 tx.onabort = function onabort() {
57 tx.onabort = function onabort() {
40 f();
58 f();
41 };
59 };
60+ success();
61+
62+ return function canceler(_, cancelerError) {
63+ cancelerError(new Error("Can't cancel IDB Effects"));
64+ };
42 };
65 };
43};
66};
44
67
45−exports._onComplete = function _onComplete(tx, f) {
68+export function _onComplete(tx, f) {
46− return function eff() {
69+ return function aff(error, success) {
47 tx.oncomplete = function oncomplete() {
70 tx.oncomplete = function oncomplete() {
48 f();
71 f();
49 };
72 };
73+ success();
74+
75+ return function canceler(_, cancelerError) {
76+ cancelerError(new Error("Can't cancel IDB Effects"));
77+ };
50 };
78 };
51};
79};
52
80
53−exports._onError = function _onError(tx, f) {
81+export function _onError(tx, f) {
54− return function eff() {
82+ return function aff(error, success) {
55 tx.onerror = function onerror(e) {
83 tx.onerror = function onerror(e) {
56− f(new Error(e.target.error.name))();
84+ f(e.target.error)();
85+ };
86+ success();
87+
88+ return function canceler(_, cancelerError) {
89+ cancelerError(new Error("Can't cancel IDB Effects"));
57 };
90 };
58 };
91 };
59};
92};
src/Database/IndexedDB/IDBTransaction.purs+111−51
@@ -1,93 +1,153 @@
1+-- | An object store is the primary storage mechanism for storing data in a database.
1module Database.IndexedDB.IDBTransaction
2module Database.IndexedDB.IDBTransaction
3+ -- * Interface
4+ ( abort
2− (class IDBTransaction, abort, objectStore
5+ , objectStore
6+
7+ -- * Attributes
3 , error
8 , error
4 , mode
9 , mode
10+
11+ -- * Event Handlers
5 , onAbort
12 , onAbort
6 , onComplete
13 , onComplete
7 , onError
14 , onError
8 ) where
15 ) where
9
16
10−import Prelude (Unit, (>>>))
11−
12−import Control.Monad.Eff (Eff)
13−import Control.Monad.Eff.Exception (EXCEPTION, Error)
14−import Data.Function.Uncurried as Fn
15−import Data.Function.Uncurried (Fn2, Fn4)
16−import Data.Maybe (Maybe)
17−import Data.Nullable (Nullable, toMaybe)
18−
19−import Database.IndexedDB.Core (IDB, ObjectStore, Transaction, TransactionMode(..))
17+import Database.IndexedDB.Core
20
18
19+import Data.Function.Uncurried (Fn2, Fn4)
20+import Data.Function.Uncurried as Fn
21+import Data.Maybe (Maybe)
22+import Data.Nullable (Nullable, toMaybe)
23+import Effect (Effect)
24+import Effect.Aff (Aff)
25+import Effect.Aff.Compat (EffectFnAff, fromEffectFnAff)
26+import Effect.Exception (Error)
27+import Prelude (Unit, ($), (<<<), (>>>))
21
28
22--------------------
29--------------------
23-- INTERFACES
30-- INTERFACES
24--
31--
25−class IDBTransaction tx where
26− abort :: forall e. tx -> Eff (idb :: IDB, exception :: EXCEPTION | e) Unit
27− objectStore :: forall e. tx -> String -> Eff (idb :: IDB, exception :: EXCEPTION | e) ObjectStore
28
32
33+-- | Aborts the transaction. All pending requests will fail with a "AbortError"
34+-- | DOMException and all changes made to the database will be reverted.
35+abort
36+ :: forall tx
37+ . (IDBTransaction tx)
38+ => tx
39+ -> Aff Unit
40+abort =
41+ fromEffectFnAff <<< _abort
42+
43+-- | Returns an IDBObjectStore in the transaction's scope.
44+objectStore
45+ :: forall tx
46+ . (IDBTransaction tx)
47+ => tx
48+ -> String
49+ -> Aff ObjectStore
50+objectStore tx name =
51+ fromEffectFnAff $ Fn.runFn2 _objectStore tx name
29
52
30--------------------
53--------------------
31-- ATTRIBUTES
54-- ATTRIBUTES
32--
55--
56+
57+--- | Returns the transaction’s connection.
58+db
59+ :: Transaction
60+ -> Database
61+db =
62+ _db
63+
64+-- | If the transaction was aborted, returns the error (a DOMException) providing the reason.
65+error
33−error :: Transaction -> Maybe Error
66+ :: Transaction
67+ -> Maybe Error
34error =
68error =
35 _error >>> toMaybe
69 _error >>> toMaybe
36
70
37−
71+-- | Returns the mode the transaction was created with (`ReadOnly|ReadWrite`)
72+-- | , or `VersionChange` for an upgrade transaction.
73+mode
74+ :: Transaction
38−mode :: Transaction -> TransactionMode
75+ -> TransactionMode
39mode =
76mode =
40 Fn.runFn4 _mode ReadOnly ReadWrite VersionChange
77 Fn.runFn4 _mode ReadOnly ReadWrite VersionChange
41
78
79+-- | Returns a list of the names of object stores in the transaction’s scope.
80+-- | For an upgrade transaction this is all object stores in the database.
81+objectStoreNames
82+ :: Transaction
83+ -> Array String
84+objectStoreNames =
85+ _objectStoreNames
42
86
43--------------------
87--------------------
44-- EVENT HANDLERS
88-- EVENT HANDLERS
45--
89--
46−onAbort :: forall e e'. Transaction -> Eff ( | e') Unit -> Eff (idb :: IDB | e) Unit
47−onAbort db f =
48− Fn.runFn2 _onAbort db f
49−
50−
51−onComplete :: forall e e'. Transaction -> Eff ( | e') Unit -> Eff (idb :: IDB | e) Unit
52−onComplete db f =
53− Fn.runFn2 _onComplete db f
54−
55−
56−onError :: forall e e'. Transaction -> (Error -> Eff ( | e') Unit) -> Eff (idb :: IDB | e) Unit
57−onError db f =
58− Fn.runFn2 _onError db f
59−
60−
61−--------------------
62−-- INSTANCES
63−--
64−instance idbTransactionTransaction :: IDBTransaction Transaction where
65− objectStore tx name =
66− Fn.runFn2 _objectStore tx name
67−
68− abort =
69− _abort
70
90
91+-- | Event handler for the `abort` event.
92+onAbort
93+ :: Transaction
94+ -> Effect Unit
95+ -> Aff Unit
96+onAbort db' f =
97+ fromEffectFnAff $ Fn.runFn2 _onAbort db' f
98+
99+-- | Event handler for the `complete` event.
100+onComplete
101+ :: Transaction
102+ -> Effect Unit
103+ -> Aff Unit
104+onComplete db' f =
105+ fromEffectFnAff $ Fn.runFn2 _onComplete db' f
106+
107+-- | Event handler for the `error` event.
108+onError
109+ :: Transaction
110+ -> (Error -> Effect Unit)
111+ -> Aff Unit
112+onError db' f =
113+ fromEffectFnAff $ Fn.runFn2 _onError db' f
71
114
72--------------------
115--------------------
73-- FFI
116-- FFI
74--
117--
75−foreign import _abort :: forall tx e. tx -> Eff (idb :: IDB, exception :: EXCEPTION | e) Unit
76−
77−
78−foreign import _error :: Transaction -> (Nullable Error)
79−
80
118
81−foreign import _mode :: Fn4 TransactionMode TransactionMode TransactionMode Transaction TransactionMode
119+foreign import _abort
120+ :: forall tx
121+ . tx
122+ -> EffectFnAff Unit
82
123
124+foreign import _db
125+ :: Transaction
126+ -> Database
83
127
84−foreign import _objectStore :: forall tx e. Fn2 tx String (Eff (idb :: IDB, exception :: EXCEPTION | e) ObjectStore)
128+foreign import _error
129+ :: Transaction
130+ -> (Nullable Error)
85
131
132+foreign import _mode
133+ :: Fn4 TransactionMode TransactionMode TransactionMode Transaction TransactionMode
86
134
87−foreign import _onAbort :: forall tx e e'. Fn2 tx (Eff ( | e') Unit) (Eff (idb :: IDB | e) Unit)
135+foreign import _objectStoreNames
136+ :: Transaction
137+ -> Array String
88
138
139+foreign import _objectStore
140+ :: forall tx
141+ . Fn2 tx String (EffectFnAff ObjectStore)
89
142
90−foreign import _onComplete :: forall tx e e'. Fn2 tx (Eff ( | e') Unit) (Eff (idb :: IDB | e) Unit)
143+foreign import _onAbort
144+ :: forall tx
145+ . Fn2 tx (Effect Unit) (EffectFnAff Unit)
91
146
147+foreign import _onComplete
148+ :: forall tx
149+ . Fn2 tx (Effect Unit) (EffectFnAff Unit)
92
150
93−foreign import _onError :: forall tx e e'. Fn2 tx (Error -> Eff ( | e') Unit) (Eff (idb :: IDB | e) Unit)
151+foreign import _onError
152+ :: forall tx
153+ . Fn2 tx (Error -> Effect Unit) (EffectFnAff Unit)
src/Main.pursdeleted−24
@@ -1,24 +0,0 @@
1−module Main where
2−
3−import Prelude
4−
5−import Control.Monad.Aff as Aff
6−import Control.Monad.Eff(kind Effect, Eff)
7−import Control.Monad.Eff.Console as Console
8−import Control.Monad.Eff.Console(CONSOLE)
9−import Control.Monad.Eff.Exception(EXCEPTION, catchException)
10−import Data.Maybe(Maybe(..))
11−import Data.Either(Either(..))
12−
13−import Core
14−import IDBDatabase as IDBDatabase
15−
16−
17−main :: Eff (exception :: EXCEPTION, idb :: INDEXED_DB, console :: CONSOLE) Unit
18−main = do
19− _ <- Aff.runAff (show >>> Console.log) (IDBDatabase.name >>> Console.log) $ IDBDatabase.open "myDatabase" Nothing
20− { onBlocked : Nothing
21− , onUpgradeNeeded : Nothing
22− }
23−
24− pure unit
src/Test/Spec/Mocha.jsdeleted−46
@@ -1,46 +0,0 @@
1−/* global exports, it */
2−
3−// module Test.Spec.Mocha
4−
5−if (typeof describe !== 'function' || typeof it !== 'function') {
6− throw new Error('Mocha globals seem to be unavailable!');
7−}
8−
9−exports.itAsync = function (only) {
10− "use strict";
11− return function (name) {
12− return function (aff) {
13− return function () {
14− var f = only ? it.only : it;
15− f(name, function (done) {
16− aff(function () {
17− done();
18− }, function (err) {
19− done(err);
20− });
21− });
22− };
23− };
24− };
25−};
26−
27−exports.itPending = function (name) {
28− "use strict";
29− return function () {
30− it(name);
31− };
32−};
33−
34−exports.describe = function (only) {
35− "use strict";
36− return function (name) {
37− return function (nested) {
38− return function () {
39− var f = only ? describe : describe.only;
40− f(name, function () {
41− nested();
42− });
43− };
44− };
45− };
46−};
src/Test/Spec/Mocha.pursdeleted−41
@@ -1,41 +0,0 @@
1−module Test.Spec.Mocha (
2− runMocha,
3− MOCHA()
4− ) where
5−
6−import Prelude
7−
8−import Control.Monad.Aff (Aff())
9−import Control.Monad.Eff (kind Effect, Eff())
10−
11−import Data.Foldable (traverse_)
12−
13−import Test.Spec (Spec, Group(..), collect)
14−
15−foreign import data MOCHA :: Effect
16−
17−foreign import itAsync :: forall e.
18− Boolean
19− -> String
20− -> Aff e Unit
21− -> Eff (mocha :: MOCHA | e) Unit
22−
23−foreign import itPending :: forall e. String
24− -> Eff (mocha :: MOCHA | e) Unit
25−
26−foreign import describe :: forall e.
27− Boolean
28− -> String
29− -> Eff (mocha :: MOCHA | e) Unit
30− -> Eff (mocha :: MOCHA | e) Unit
31−
32−registerGroup :: forall e. (Group (Aff e Unit))
33− -> Eff (mocha :: MOCHA | e) Unit
34−registerGroup (It only name test) = itAsync only name test
35−registerGroup (Pending name) = itPending name
36−registerGroup (Describe only name groups) =
37− describe only name (traverse_ registerGroup groups)
38−
39−runMocha :: forall e. Spec e Unit
40− -> Eff (mocha :: MOCHA | e) Unit
41−runMocha spec = traverse_ registerGroup (collect spec)
test/Main.purs+1087−875
@@ -2,928 +2,1140 @@ module Test.Main where
2
2
3import Prelude
3import Prelude
4
4
5−import Control.Monad.Aff (Aff, launchAff, forkAff, delay)
5+import Data.Array (head, drop)
6−import Control.Monad.Aff.Console (log)
6+import Data.Date as Date
7−import Control.Monad.Aff.AVar (AVAR, makeVar', modifyVar, peekVar, killVar)
7+import Data.DateTime (DateTime(..), Time(..))
8−import Control.Monad.Eff (Eff)
9−import Control.Monad.Eff.Class (liftEff)
10−import Control.Monad.Eff.Exception (EXCEPTION)
11−import Data.Date as Date
12−import Data.DateTime as DateTime
8+import Data.DateTime.Instant (toDateTime)
13−import Data.DateTime (DateTime(..), Date(..), Time(..))
9+import Data.Either (Either(..))
14−import Data.Enum (toEnum)
10+import Data.Enum (toEnum)
15−import Data.Maybe (Maybe(..), isNothing)
11+import Data.Maybe (Maybe(..), isNothing, maybe)
16−import Data.Time.Duration (Milliseconds(..))
12+import Data.Time.Duration (Milliseconds(..))
17−import Test.Spec (describe, it)
13+import Data.Traversable (traverse)
18−import Test.Spec.Assertions (shouldEqual, fail)
14+import Data.Tuple (Tuple(..), uncurry)
19−import Test.Spec.Mocha (MOCHA, runMocha)
20−
21−import Database.IndexedDB.Core
15+import Database.IndexedDB.Core (CursorDirection(..), Database, Index, ObjectStore, Transaction, TransactionMode(..))
16+import Database.IndexedDB.IDBCursor as IDBCursor
17+import Database.IndexedDB.IDBDatabase as IDBDatabase
18+import Database.IndexedDB.IDBFactory as IDBFactory
19+import Database.IndexedDB.IDBIndex as IDBIndex
22−import Database.IndexedDB.IDBKey
20+import Database.IndexedDB.IDBKey (Key, none, toKey)
23−import Database.IndexedDB.IDBDatabase as IDBDatabase
21+import Database.IndexedDB.IDBKeyRange as IDBKeyRange
24−import Database.IndexedDB.IDBFactory as IDBFactory
22+import Database.IndexedDB.IDBObjectStore as IDBObjectStore
25−import Database.IndexedDB.IDBObjectStore as IDBObjectStore
23+import Database.IndexedDB.IDBTransaction as IDBTransaction
26−import Database.IndexedDB.IDBKeyRange as IDBKeyRange
24+import Effect (Effect)
27−
25+import Effect.Aff (Aff, attempt, delay, forkAff, launchAff, launchAff_)
28−
26+import Effect.Aff.AVar (AVar)
29−tearDown :: forall eff. String -> Int -> Database -> Aff (idb :: INDEXED_DB, exception :: EXCEPTION | eff) Unit
27+import Effect.Aff.AVar as AVar
28+import Effect.Class (liftEffect)
29+import Effect.Console (log)
30+import Effect.Exception (name)
31+import Effect.Now (now)
32+import Test.Spec (describe, it)
33+import Test.Spec.Assertions (shouldEqual, fail)
34+import Test.Spec.Reporter (consoleReporter)
35+import Test.Spec.Runner (runSpec)
36+
37+infixr 7 Tuple as :+:
38+
39+launchAff' :: forall a. Aff a -> Effect Unit
40+launchAff' =
41+ launchAff_ <<< void
42+
43+modifyAVar :: forall a. (a -> a) -> AVar a -> Aff Unit
44+modifyAVar fn v = do
45+ val <- AVar.take v
46+ AVar.put (fn val) v
47+
48+main :: Effect Unit
49+main = do
50+ launchAff_ $ runSpec [ consoleReporter ] $ do
51+ describe "IDBFactory" do
52+ let
30−tearDown name version db = do
53+ tearDown name version db = do
31− liftEff $ IDBDatabase.close db
54+ IDBDatabase.close db
32− version' <- IDBFactory.deleteDatabase name
55+ version' <- IDBFactory.deleteDatabase name
33− version' `shouldEqual` version
56+ liftEffect $ log $ show version'
34−
57+ version' `shouldEqual` version
35−-- main :: forall eff. Eff (mocha :: MOCHA, idb :: INDEXED_DB, exception :: EXCEPTION, avar :: AVAR | eff) Unit
58+
36−main = runMocha do
37− describe "IDBFactory" do
38− it "open default" do
59+ it "open default" do
60+ let
39− let name = "db-default"
61+ name = "db-default"
40 version = 1
62 version = 1
41− db <- IDBFactory.open name Nothing
63+ db <- IDBFactory.open name Nothing
42− { onUpgradeNeeded : Nothing
64+ { onUpgradeNeeded: Nothing
43− , onBlocked : Nothing
65+ , onBlocked: Nothing
44− }
66+ }
45− IDBDatabase.name db `shouldEqual` name
67+ IDBDatabase.name db `shouldEqual` name
46− tearDown name version db
68+ tearDown name version db
47−
69+
48−
49− it "open specific version" do
70+ it "open specific version" do
71+ let
50− let name = "db-specific"
72+ name = "db-specific"
51 version = 14
73 version = 14
52− db <- IDBFactory.open name (Just version)
74+ db <- IDBFactory.open name (Just version)
53− { onUpgradeNeeded : Nothing
75+ { onUpgradeNeeded: Nothing
54− , onBlocked : Nothing
76+ , onBlocked: Nothing
55− }
77+ }
56− IDBDatabase.name db `shouldEqual` name
78+ IDBDatabase.name db `shouldEqual` name
57− tearDown name version db
79+ tearDown name version db
58−
80+
59−
60− it "open specific version -> close -> open latest" do
81+ it "open specific version -> close -> open latest" do
82+ let
61− let name = "db-latest"
83+ name = "db-latest"
62 version = 14
84 version = 14
63− db <- IDBFactory.open name (Just version)
85+ db01 <- IDBFactory.open name (Just version)
64− { onUpgradeNeeded : Nothing
86+ { onUpgradeNeeded: Nothing
65− , onBlocked : Nothing
87+ , onBlocked: Nothing
66− }
88+ }
67− IDBDatabase.name db `shouldEqual` name
89+ IDBDatabase.name db01 `shouldEqual` name
68− liftEff $ IDBDatabase.close db
90+ IDBDatabase.close db01
69− db <- IDBFactory.open name Nothing
91+ db02 <- IDBFactory.open name Nothing
70− { onUpgradeNeeded : Nothing
92+ { onUpgradeNeeded: Nothing
71− , onBlocked : Nothing
93+ , onBlocked: Nothing
72− }
94+ }
73− tearDown name version db
95+ tearDown name version db02
74−
96+
75−
76− it "open + onUpgradeNeed" do
97+ it "open + onUpgradeNeed" do
98+ let
77− let name = "db-upgrade-needed"
99+ name = "db-upgrade-needed"
78 version = 1
100 version = 1
79− callback var db = do
101+ callback (Tuple varName varVersion) db _ { oldVersion } = do
80− _ <- launchAff $ modifyVar (const $ IDBDatabase.name db) var
102+ _ <- launchAff $ modifyAVar (const $ IDBDatabase.name db) varName
103+ _ <- launchAff $ modifyAVar (const $ oldVersion) varVersion
81 pure unit
104 pure unit
82− var <- makeVar' "-"
105+ varName <- AVar.new "-"
106+ varVersion <- AVar.new (-1)
83− db <- IDBFactory.open name Nothing
107+ db <- IDBFactory.open name Nothing
84− { onUpgradeNeeded : Just (callback var)
108+ { onUpgradeNeeded: Just (callback (Tuple varName varVersion))
85− , onBlocked : Nothing
109+ , onBlocked: Nothing
86− }
110+ }
87− name <- peekVar var
111+ name' <- AVar.read varName
112+ version' <- AVar.read varVersion
88− name `shouldEqual` name
113+ name' `shouldEqual` name
114+ version' `shouldEqual` 0
89− tearDown name version db
115+ tearDown name version db
90−
116+
91−
92− it "open + onBlocked" do
117+ it "open + onBlocked" do
118+ let
93− let name = "db-blocked"
119+ name = "db-blocked"
94 version = 14
120 version = 14
95 callback var = do
121 callback var = do
96− _ <- launchAff $ modifyVar (const $ "db-blocked") var
122+ _ <- launchAff $ modifyAVar (const $ "db-blocked") var
97 pure unit
123 pure unit
98
124
99− var <- makeVar' "-"
125+ var <- AVar.new "-"
100− db01 <- IDBFactory.open name Nothing
126+ db01 <- IDBFactory.open name Nothing
101− { onUpgradeNeeded : Nothing
127+ { onUpgradeNeeded: Nothing
102− , onBlocked : Nothing
128+ , onBlocked: Nothing
103− }
129+ }
104− _ <- forkAff do
130+ _ <- forkAff do
105− delay (Milliseconds 100.0)
131+ delay (Milliseconds 100.0)
106− liftEff $ IDBDatabase.close db01
132+ IDBDatabase.close db01
107−
133+
108− db02 <- IDBFactory.open name (Just version)
134+ db02 <- IDBFactory.open name (Just version)
109− { onUpgradeNeeded : Nothing
135+ { onUpgradeNeeded: Nothing
110− , onBlocked : Just (callback var)
136+ , onBlocked: Just (callback var)
111− }
137+ }
112− name <- peekVar var
138+ name' <- AVar.read var
113− name `shouldEqual` name
139+ name' `shouldEqual` name
114− tearDown name version db02
140+ tearDown name version db02
115−
141+
116− describe "IDBKeyRange" do
142+ describe "IDBKeyRange" do
117− it "only(int)" do
143+ it "only(int)" do
144+ let
118− let key = 14
145+ key = 14
119 range = IDBKeyRange.only key
146 range = IDBKeyRange.only key
120− IDBKeyRange.includes range (toKey key) `shouldEqual` true
147+ IDBKeyRange.includes range (toKey key) `shouldEqual` true
121
148
122− it "only(string)" do
149+ it "only(string)" do
150+ let
123− let key = "patate"
151+ key = "patate"
124 range = IDBKeyRange.only key
152 range = IDBKeyRange.only key
125− IDBKeyRange.includes range (toKey key) `shouldEqual` true
153+ IDBKeyRange.includes range (toKey key) `shouldEqual` true
126
154
127− it "only(float)" do
155+ it "only(float)" do
156+ let
128− let key = 14.42
157+ key = 14.42
129 range = IDBKeyRange.only key
158 range = IDBKeyRange.only key
130− IDBKeyRange.includes range (toKey key) `shouldEqual` true
159+ IDBKeyRange.includes range (toKey key) `shouldEqual` true
131
160
132− it "only(date)" do
161+ it "only(date)" do
162+ let
133− let mkey = DateTime
163+ mkey = DateTime
134 <$> (Date.canonicalDate <$> toEnum 2017 <*> toEnum 6 <*> toEnum 23)
164 <$> (Date.canonicalDate <$> toEnum 2017 <*> toEnum 6 <*> toEnum 23)
135 <*> (Time <$> toEnum 17 <*> toEnum 59 <*> toEnum 34 <*> toEnum 42)
165 <*> (Time <$> toEnum 17 <*> toEnum 59 <*> toEnum 34 <*> toEnum 42)
136
166
137− case mkey of
167+ case mkey of
138− Nothing ->
168+ Nothing ->
139− fail "unable to create datetime"
169+ fail "unable to create datetime"
140− Just key -> do
170+ Just key -> do
141− let range = IDBKeyRange.only key
171+ let range = IDBKeyRange.only key
142− IDBKeyRange.includes range (toKey key) `shouldEqual` true
172+ IDBKeyRange.includes range (toKey key) `shouldEqual` true
143
173
144− it "only([int])" do
174+ it "only([int])" do
175+ let
145− let key = [14, 42]
176+ key = [ 14, 42 ]
146 range = IDBKeyRange.only key
177 range = IDBKeyRange.only key
147− IDBKeyRange.includes range (toKey key) `shouldEqual` true
178+ IDBKeyRange.includes range (toKey key) `shouldEqual` true
148
179
149− it "only([string])" do
180+ it "only([string])" do
181+ let
150− let key = ["patate", "autruche"]
182+ key = [ "patate", "autruche" ]
151 range = IDBKeyRange.only key
183 range = IDBKeyRange.only key
152− IDBKeyRange.includes range (toKey key) `shouldEqual` true
184+ IDBKeyRange.includes range (toKey key) `shouldEqual` true
153
185
154− it "lowerBound(14, open)" do
186+ it "lowerBound(14, open)" do
187+ let
155− let key = 14
188+ key = 14
156− open = true
189+ open = true
157 range = IDBKeyRange.lowerBound key open
190 range = IDBKeyRange.lowerBound key open
158− IDBKeyRange.includes range (toKey (key + 1)) `shouldEqual` true
191+ IDBKeyRange.includes range (toKey (key + 1)) `shouldEqual` true
159− IDBKeyRange.includes range (toKey key) `shouldEqual` (not open)
192+ IDBKeyRange.includes range (toKey key) `shouldEqual` (not open)
160− IDBKeyRange.includes range (toKey (key - 1)) `shouldEqual` false
193+ IDBKeyRange.includes range (toKey (key - 1)) `shouldEqual` false
161−
194+
162− it "lowerBound(14, close)" do
195+ it "lowerBound(14, close)" do
196+ let
163− let key = 14
197+ key = 14
164− open = false
198+ open = false
165 range = IDBKeyRange.lowerBound key open
199 range = IDBKeyRange.lowerBound key open
166− IDBKeyRange.includes range (toKey (key + 1)) `shouldEqual` true
200+ IDBKeyRange.includes range (toKey (key + 1)) `shouldEqual` true
167− IDBKeyRange.includes range (toKey key) `shouldEqual` (not open)
201+ IDBKeyRange.includes range (toKey key) `shouldEqual` (not open)
168− IDBKeyRange.includes range (toKey (key - 1)) `shouldEqual` false
202+ IDBKeyRange.includes range (toKey (key - 1)) `shouldEqual` false
169−
203+
170− it "upperBound(14, open)" do
204+ it "upperBound(14, open)" do
205+ let
171− let key = 14
206+ key = 14
172− open = true
207+ open = true
173 range = IDBKeyRange.upperBound key open
208 range = IDBKeyRange.upperBound key open
174− IDBKeyRange.includes range (toKey (key + 1)) `shouldEqual` false
209+ IDBKeyRange.includes range (toKey (key + 1)) `shouldEqual` false
175− IDBKeyRange.includes range (toKey key) `shouldEqual` (not open)
210+ IDBKeyRange.includes range (toKey key) `shouldEqual` (not open)
176− IDBKeyRange.includes range (toKey (key - 1)) `shouldEqual` true
211+ IDBKeyRange.includes range (toKey (key - 1)) `shouldEqual` true
177−
212+
178− it "upperBound(14, close)" do
213+ it "upperBound(14, close)" do
214+ let
179− let key = 14
215+ key = 14
180− open = false
216+ open = false
181 range = IDBKeyRange.upperBound key open
217 range = IDBKeyRange.upperBound key open
182− IDBKeyRange.includes range (toKey (key + 1)) `shouldEqual` false
218+ IDBKeyRange.includes range (toKey (key + 1)) `shouldEqual` false
183− IDBKeyRange.includes range (toKey key) `shouldEqual` (not open)
219+ IDBKeyRange.includes range (toKey key) `shouldEqual` (not open)
184− IDBKeyRange.includes range (toKey (key - 1)) `shouldEqual` true
220+ IDBKeyRange.includes range (toKey (key - 1)) `shouldEqual` true
185−
221+
186− it "bound(42, 14, open, open) => Nothing" do
222+ it "bound(42, 14, open, open) => Nothing" do
223+ let
187− let lower = 42
224+ lower = 42
188− upper = 14
225+ upper = 14
189 lowerOpen = true
226 lowerOpen = true
190 upperOpen = true
227 upperOpen = true
191− mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
228+ mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
192− isNothing mrange `shouldEqual` true
229+ isNothing mrange `shouldEqual` true
193
230
194− it "bound(14, 42, open, open)" do
231+ it "bound(14, 42, open, open)" do
232+ let
195− let lower = 14
233+ lower = 14
196− upper = 42
234+ upper = 42
197 lowerOpen = true
235 lowerOpen = true
198 upperOpen = true
236 upperOpen = true
199− mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
237+ mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
200− case mrange of
238+ case mrange of
201− Nothing ->
239+ Nothing ->
202− fail "invalid range provided"
240+ fail "invalid range provided"
203− Just range -> do
241+ Just range -> do
204− IDBKeyRange.includes range (toKey (lower + 1)) `shouldEqual` true
242+ IDBKeyRange.includes range (toKey (lower + 1)) `shouldEqual` true
205− IDBKeyRange.includes range (toKey (upper - 1)) `shouldEqual` true
243+ IDBKeyRange.includes range (toKey (upper - 1)) `shouldEqual` true
206− IDBKeyRange.includes range (toKey (lower - 1)) `shouldEqual` false
244+ IDBKeyRange.includes range (toKey (lower - 1)) `shouldEqual` false
207− IDBKeyRange.includes range (toKey (upper + 1)) `shouldEqual` false
245+ IDBKeyRange.includes range (toKey (upper + 1)) `shouldEqual` false
208− IDBKeyRange.includes range (toKey lower) `shouldEqual` (not lowerOpen)
246+ IDBKeyRange.includes range (toKey lower) `shouldEqual` (not lowerOpen)
209− IDBKeyRange.includes range (toKey upper) `shouldEqual` (not upperOpen)
247+ IDBKeyRange.includes range (toKey upper) `shouldEqual` (not upperOpen)
210−
248+
211− it "bound(14, 42, open, close)" do
249+ it "bound(14, 42, open, close)" do
250+ let
212− let lower = 14
251+ lower = 14
213− upper = 42
252+ upper = 42
214 lowerOpen = true
253 lowerOpen = true
215 upperOpen = false
254 upperOpen = false
216− mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
255+ mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
217− case mrange of
256+ case mrange of
218− Nothing ->
257+ Nothing ->
219− fail "invalid range provided"
258+ fail "invalid range provided"
220− Just range -> do
259+ Just range -> do
221− IDBKeyRange.includes range (toKey (lower + 1)) `shouldEqual` true
260+ IDBKeyRange.includes range (toKey (lower + 1)) `shouldEqual` true
222− IDBKeyRange.includes range (toKey (upper - 1)) `shouldEqual` true
261+ IDBKeyRange.includes range (toKey (upper - 1)) `shouldEqual` true
223− IDBKeyRange.includes range (toKey (lower - 1)) `shouldEqual` false
262+ IDBKeyRange.includes range (toKey (lower - 1)) `shouldEqual` false
224− IDBKeyRange.includes range (toKey (upper + 1)) `shouldEqual` false
263+ IDBKeyRange.includes range (toKey (upper + 1)) `shouldEqual` false
225− IDBKeyRange.includes range (toKey lower) `shouldEqual` (not lowerOpen)
264+ IDBKeyRange.includes range (toKey lower) `shouldEqual` (not lowerOpen)
226− IDBKeyRange.includes range (toKey upper) `shouldEqual` (not upperOpen)
265+ IDBKeyRange.includes range (toKey upper) `shouldEqual` (not upperOpen)
227−
266+
228− it "bound(14, 42, close, open)" do
267+ it "bound(14, 42, close, open)" do
268+ let
229− let lower = 14
269+ lower = 14
230− upper = 42
270+ upper = 42
231 lowerOpen = false
271 lowerOpen = false
232 upperOpen = true
272 upperOpen = true
233− mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
273+ mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
234− case mrange of
274+ case mrange of
235− Nothing ->
275+ Nothing ->
236− fail "invalid range provided"
276+ fail "invalid range provided"
237− Just range -> do
277+ Just range -> do
238− IDBKeyRange.includes range (toKey (lower + 1)) `shouldEqual` true
278+ IDBKeyRange.includes range (toKey (lower + 1)) `shouldEqual` true
239− IDBKeyRange.includes range (toKey (upper - 1)) `shouldEqual` true
279+ IDBKeyRange.includes range (toKey (upper - 1)) `shouldEqual` true
240− IDBKeyRange.includes range (toKey (lower - 1)) `shouldEqual` false
280+ IDBKeyRange.includes range (toKey (lower - 1)) `shouldEqual` false
241− IDBKeyRange.includes range (toKey (upper + 1)) `shouldEqual` false
281+ IDBKeyRange.includes range (toKey (upper + 1)) `shouldEqual` false
242− IDBKeyRange.includes range (toKey lower) `shouldEqual` (not lowerOpen)
282+ IDBKeyRange.includes range (toKey lower) `shouldEqual` (not lowerOpen)
243− IDBKeyRange.includes range (toKey upper) `shouldEqual` (not upperOpen)
283+ IDBKeyRange.includes range (toKey upper) `shouldEqual` (not upperOpen)
244−
284+
245− it "bound(14, 42, close, close)" do
285+ it "bound(14, 42, close, close)" do
286+ let
246− let lower = 14
287+ lower = 14
247− upper = 42
288+ upper = 42
248 lowerOpen = false
289 lowerOpen = false
249 upperOpen = false
290 upperOpen = false
250− mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
291+ mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
251− case mrange of
292+ case mrange of
252− Nothing ->
293+ Nothing ->
253− fail "invalid range provided"
294+ fail "invalid range provided"
254− Just range -> do
295+ Just range -> do
255− IDBKeyRange.includes range (toKey (lower + 1)) `shouldEqual` true
296+ IDBKeyRange.includes range (toKey (lower + 1)) `shouldEqual` true
256− IDBKeyRange.includes range (toKey (upper - 1)) `shouldEqual` true
297+ IDBKeyRange.includes range (toKey (upper - 1)) `shouldEqual` true
257− IDBKeyRange.includes range (toKey (lower - 1)) `shouldEqual` false
298+ IDBKeyRange.includes range (toKey (lower - 1)) `shouldEqual` false
258− IDBKeyRange.includes range (toKey (upper + 1)) `shouldEqual` false
299+ IDBKeyRange.includes range (toKey (upper + 1)) `shouldEqual` false
259− IDBKeyRange.includes range (toKey lower) `shouldEqual` (not lowerOpen)
300+ IDBKeyRange.includes range (toKey lower) `shouldEqual` (not lowerOpen)
260− IDBKeyRange.includes range (toKey upper) `shouldEqual` (not upperOpen)
301+ IDBKeyRange.includes range (toKey upper) `shouldEqual` (not upperOpen)
261−
302+
262− it "can access attributes of a range" do
303+ it "can access attributes of a range" do
263− let range = IDBKeyRange.lowerBound 14 true
304+ let range = IDBKeyRange.lowerBound 14 false
264− IDBKeyRange.lower range `shouldEqual` (Just $ toKey 14)
305+ IDBKeyRange.lower range `shouldEqual` (Just $ toKey 14)
265− IDBKeyRange.upper range `shouldEqual` (Nothing :: Maybe Key)
306+ IDBKeyRange.upper range `shouldEqual` none
266− IDBKeyRange.lowerOpen range `shouldEqual` true
307+ IDBKeyRange.lowerOpen range `shouldEqual` false
267− -- NOTE Not true on Chrome 56.x => The default value for the flag is `true`
268− -- IDBKeyRange.upperOpen range `shouldEqual` false
308+ IDBKeyRange.upperOpen range `shouldEqual` true
269−
309+
270− describe "IDBDatabase" do
310+ describe "IDBDatabase" do
271− it "createObjectStore (keyPath: [], autoIncrement: true)" do
311+ let
272− let dbName = "db-create-store-01"
312+ tearDown db = do
273− dbVersion = 1
274− storeName = "store"
275− keyPath = []
276− autoIncrement = true
277− callback var db = do
278− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
279− _ <- launchAff $ IDBObjectStore.name store `shouldEqual` storeName
280− _ <- launchAff $ IDBObjectStore.keyPath store `shouldEqual` keyPath
281− _ <- launchAff $ IDBObjectStore.autoIncrement store `shouldEqual` autoIncrement
282− _ <- launchAff $ IDBObjectStore.indexNames store `shouldEqual` []
283− _ <- launchAff $ modifyVar (const true) var
284− pure unit
285−
286− var <- makeVar' false
287− db <- IDBFactory.open dbName Nothing
288− { onUpgradeNeeded : Just (callback var)
289− , onBlocked : Nothing
290− }
291− hasBeenCalled <- peekVar var
292− hasBeenCalled `shouldEqual` true
293− tearDown dbName dbVersion db
294−
295−
296− it "createObjectStore (keyPath: [\"field\"], autoIncrement: true)" do
297− let dbName = "db-create-store-02"
298− dbVersion = 1
299− storeName = "store"
300− keyPath = ["field"]
301− autoIncrement = true
302− callback var db = do
303− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
304− _ <- launchAff $ IDBObjectStore.name store `shouldEqual` storeName
305− _ <- launchAff $ IDBObjectStore.keyPath store `shouldEqual` keyPath
306− _ <- launchAff $ IDBObjectStore.autoIncrement store `shouldEqual` autoIncrement
307− _ <- launchAff $ IDBObjectStore.indexNames store `shouldEqual` []
308− _ <- launchAff $ modifyVar (const true) var
309− pure unit
310−
311− var <- makeVar' false
312− db <- IDBFactory.open dbName Nothing
313− { onUpgradeNeeded : Just (callback var)
314− , onBlocked : Nothing
315− }
316− hasBeenCalled <- peekVar var
317− hasBeenCalled `shouldEqual` true
318− tearDown dbName dbVersion db
319−
320−
321− it "createObjectStore (keyPath: [\"a\", \"b\"], autoIncrement: false)" do
322− let dbName = "db-create-store-03"
323− dbVersion = 1
324− storeName = "store"
325− keyPath = ["a", "b"]
326− autoIncrement = false
327− callback var db = do
328− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
329− _ <- launchAff $ IDBObjectStore.name store `shouldEqual` storeName
330− _ <- launchAff $ IDBObjectStore.keyPath store `shouldEqual` keyPath
331− _ <- launchAff $ IDBObjectStore.autoIncrement store `shouldEqual` autoIncrement
332− _ <- launchAff $ IDBObjectStore.indexNames store `shouldEqual` []
333− _ <- launchAff $ modifyVar (const true) var
334− pure unit
335−
336− var <- makeVar' false
337− db <- IDBFactory.open dbName Nothing
338− { onUpgradeNeeded : Just (callback var)
339− , onBlocked : Nothing
340− }
341− hasBeenCalled <- peekVar var
342− hasBeenCalled `shouldEqual` true
343− tearDown dbName dbVersion db
344−
345−
346− it "createObjectStore (keyPath: [\"field\"], autoIncrement: false)" do
347− let dbName = "db-create-store-04"
348− dbVersion = 1
349− storeName = "store"
350− keyPath = ["field"]
351− autoIncrement = false
352− callback var db = do
353− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
354− _ <- launchAff $ IDBObjectStore.name store `shouldEqual` storeName
355− _ <- launchAff $ IDBObjectStore.keyPath store `shouldEqual` keyPath
356− _ <- launchAff $ IDBObjectStore.autoIncrement store `shouldEqual` autoIncrement
357− _ <- launchAff $ IDBObjectStore.indexNames store `shouldEqual` []
358− _ <- launchAff $ modifyVar (const true) var
359− pure unit
360−
361− var <- makeVar' false
362− db <- IDBFactory.open dbName Nothing
363− { onUpgradeNeeded : Just (callback var)
364− , onBlocked : Nothing
365− }
366− hasBeenCalled <- peekVar var
367− hasBeenCalled `shouldEqual` true
368− tearDown dbName dbVersion db
369−
370−
371− it "deleteObjectStore" do
372− let dbName = "db-delete-store"
373− dbVersion = 2
374− storeName = "store"
375− keyPath = []
376− autoIncrement = true
377− callback db = do
378− _ <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
379− pure unit
380− callback2 var db = do
381− _ <- IDBDatabase.deleteObjectStore db storeName
382− _ <- launchAff $ modifyVar (const true) var
383− pure unit
384−
385− var <- makeVar' false
386− db <- IDBFactory.open dbName Nothing
387− { onUpgradeNeeded : Just callback
388− , onBlocked : Nothing
389− }
390− liftEff $ IDBDatabase.close db
313+ IDBDatabase.close db
391− db <- IDBFactory.open dbName (Just dbVersion)
314+ _ <- IDBFactory.deleteDatabase (IDBDatabase.name db)
392− { onUpgradeNeeded : Just (callback2 var)
315+ pure unit
393− , onBlocked : Nothing
316+
394− }
317+ setup storeParams = do
395− hasBeenCalled <- peekVar var
318+ let
396− hasBeenCalled `shouldEqual` true
319+ onUpgradeNeeded var db _ _ = launchAff' do
397− tearDown dbName dbVersion db
398−
399− describe "IDBObjectStore" do
400− it "add(\"patate\", Nothing )" do
401− let dbName = "db-add"
402− dbVersion = 1
403− storeName = "store"
404− keyPath = []
405− autoIncrement = true
406− key = 1
407− callback db = do
408− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
320+ store <- IDBDatabase.createObjectStore db "store" storeParams
409− key <- launchAff $ ((unsafeFromKey >>> shouldEqual key)
321+ _ <- AVar.put { db, store } var
410− <$> IDBObjectStore.add store "patate" Nothing)
411− pure unit
412−
413− db <- IDBFactory.open dbName Nothing
414− { onUpgradeNeeded : Just callback
415− , onBlocked : Nothing
416− }
417− tearDown dbName dbVersion db
418−
419− it "add(\"patate\", Just 14 )" do
420− let dbName = "db-add"
421− dbVersion = 1
422− storeName = "store"
423− keyPath = []
424− autoIncrement = true
425− key = 14
426− callback db = do
427− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
428− key <- launchAff $ ((unsafeFromKey >>> shouldEqual key)
429− <$> IDBObjectStore.add store "patate" (Just $ toKey key))
430− pure unit
431−
432− db <- IDBFactory.open dbName Nothing
433− { onUpgradeNeeded : Just callback
434− , onBlocked : Nothing
435− }
436− tearDown dbName dbVersion db
437−
438− it "add(\"patate\", Just \"key\" )" do
439− let dbName = "db-add"
440− dbVersion = 1
441− storeName = "store"
442− keyPath = []
443− autoIncrement = true
444− key = "key"
445− callback db = do
446− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
447− key <- launchAff $ ((unsafeFromKey >>> shouldEqual key)
448− <$> IDBObjectStore.add store "patate" (Just $ toKey key))
449− pure unit
322+ pure unit
450
323
324+ var <- AVar.empty
451− db <- IDBFactory.open dbName Nothing
325+ _ <- IDBFactory.open "db" Nothing
452− { onUpgradeNeeded : Just callback
326+ { onUpgradeNeeded: Just (onUpgradeNeeded var)
453− , onBlocked : Nothing
327+ , onBlocked: Nothing
454− }
328+ }
455− tearDown dbName dbVersion db
456−
457− it "add(\"patate\", Just <date> )" do
458− let dbName = "db-add"
459− dbVersion = 1
460− storeName = "store"
461− keyPath = []
462− autoIncrement = true
463− mkey = DateTime
464− <$> (Date.canonicalDate <$> toEnum 2017 <*> toEnum 6 <*> toEnum 23)
465− <*> (Time <$> toEnum 17 <*> toEnum 59 <*> toEnum 34 <*> toEnum 42)
466
329
467− case mkey of
330+ AVar.take var
331+
332+ it "createObjectStore (keyPath: [], autoIncrement: true)" do
333+ { db, store } <- setup { keyPath: [], autoIncrement: true }
334+ IDBObjectStore.name store `shouldEqual` "store"
335+ IDBObjectStore.keyPath store `shouldEqual` []
336+ IDBObjectStore.autoIncrement store `shouldEqual` true
337+ IDBObjectStore.indexNames store `shouldEqual` []
338+ tearDown db
339+
340+ it "createObjectStore (keyPath: [\"patate\"], autoIncrement: true)" do
341+ { db, store } <- setup { keyPath: [ "patate" ], autoIncrement: true }
342+ IDBObjectStore.name store `shouldEqual` "store"
343+ IDBObjectStore.keyPath store `shouldEqual` [ "patate" ]
344+ IDBObjectStore.autoIncrement store `shouldEqual` true
345+ IDBObjectStore.indexNames store `shouldEqual` []
346+ tearDown db
347+
348+ it "createObjectStore (keyPath: [\"a\", \"b\"], autoIncrement: false)" do
349+ { db, store } <- setup { keyPath: [ "patate", "autruche" ], autoIncrement: false }
350+ IDBObjectStore.name store `shouldEqual` "store"
351+ IDBObjectStore.keyPath store `shouldEqual` [ "patate", "autruche" ]
352+ IDBObjectStore.autoIncrement store `shouldEqual` false
353+ IDBObjectStore.indexNames store `shouldEqual` []
354+ tearDown db
355+
356+ it "deleteObjectStore" do
357+ let
358+ onUpgradeNeeded var db _ _ = launchAff' do
359+ _ <- IDBDatabase.deleteObjectStore db "store"
360+ AVar.put true var
361+
362+ var <- AVar.empty
363+ { db } <- setup IDBDatabase.defaultParameters
364+ IDBDatabase.close db
365+ db' <- IDBFactory.open "db" (Just 999)
366+ { onUpgradeNeeded: Just (onUpgradeNeeded var)
468− Nothing ->
367+ , onBlocked: Nothing
469− fail "unable to create datetime"
368+ }
470− Just key -> do
369+ deleted <- AVar.take var
471− let callback db = do
370+ deleted `shouldEqual` true
472− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
371+ tearDown db'
473− key <- launchAff $ ((unsafeFromKey >>> shouldEqual key)
372+
474− <$> IDBObjectStore.add store "patate" (Just $ toKey key))
373+ describe "IDBObjectStore" do
475− pure unit
374+ let
476−
375+ tearDown db = do
376+ IDBDatabase.close db
377+ _ <- IDBFactory.deleteDatabase (IDBDatabase.name db)
378+ pure unit
379+
380+ setup { storeParams, onUpgradeNeeded } = do
381+ let
382+ onUpgradeNeeded' var db _ _ = launchAff' do
383+ store <- IDBDatabase.createObjectStore db "store" storeParams
384+ liftEffect $ maybe (pure unit) identity (onUpgradeNeeded <*> pure db <*> pure store)
385+ AVar.put { db, store } var
386+
387+ var <- AVar.empty
477− db <- IDBFactory.open dbName Nothing
388+ _ <- IDBFactory.open "db" Nothing
478− { onUpgradeNeeded : Just callback
389+ { onUpgradeNeeded: Just (onUpgradeNeeded' var)
479− , onBlocked : Nothing
390+ , onBlocked: Nothing
480 }
391 }
481− tearDown dbName dbVersion db
482−
483− it "add(\"patate\", Just [14, 42] )" do
484− let dbName = "db-add"
485− dbVersion = 1
486− storeName = "store"
487− keyPath = []
488− autoIncrement = true
489− key = [14, 42]
490− callback db = do
491− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
492− key <- launchAff $ ((unsafeFromKey >>> shouldEqual key)
493− <$> IDBObjectStore.add store "patate" (Just $ toKey key))
494− pure unit
495
392
496− db <- IDBFactory.open dbName Nothing
393+ AVar.take var
497− { onUpgradeNeeded : Just callback
394+
498− , onBlocked : Nothing
499− }
500− tearDown dbName dbVersion db
501−
502− it "clear()" do
395+ it "add()" do
503− let dbName = "db-clear"
396+ date <- liftEffect $ toDateTime <$> now
504− dbVersion = 1
397+ { db } <- setup
505− storeName = "store"
506− keyPath = []
507− autoIncrement = true
398+ { storeParams: { autoIncrement: true, keyPath: [] }
508− callback db = do
399+ , onUpgradeNeeded: Just $ \_ store -> launchAff' do
509− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
400+ -- no key
401+ key <- IDBObjectStore.add store "patate" none
402+ (toKey 1) `shouldEqual` key
403+
404+ -- int key
405+ key' <- IDBObjectStore.add store "patate" (Just 14)
406+ (toKey 14) `shouldEqual` key'
407+
408+ -- number key
409+ key'' <- IDBObjectStore.add store "patate" (Just 14.42)
410+ (toKey 14.42) `shouldEqual` key''
411+
412+ -- string key
413+ key''' <- IDBObjectStore.add store "patate" (Just "key")
414+ (toKey "key") `shouldEqual` key'''
415+
416+ -- date key
417+ key'''' <- IDBObjectStore.add store "patate" (Just date)
418+ (toKey date) `shouldEqual` key''''
419+
420+ -- array key
421+ key''''' <- IDBObjectStore.add store "patate" (Just $ toKey [ 14, 42 ])
422+ (toKey [ 14, 42 ]) `shouldEqual` key'''''
423+ }
424+ tearDown db
425+
426+ it "clear()" do
427+ { db } <- setup
428+ { storeParams: IDBDatabase.defaultParameters
510− _ <- launchAff $ do
429+ , onUpgradeNeeded: Just $ \_ store -> launchAff' do
511− _ <- IDBObjectStore.add store "patate" (Just $ toKey 14)
430+ key <- IDBObjectStore.add store "patate" (Just 14)
512 _ <- IDBObjectStore.clear store
431 _ <- IDBObjectStore.clear store
513− v <- IDBObjectStore.get store (IDBKeyRange.only 14)
432+ val <- IDBObjectStore.get store (IDBKeyRange.only key)
514− v `shouldEqual` (Nothing :: Maybe Int)
433+ val `shouldEqual` (Nothing :: Maybe String)
515−
434+ }
516− pure unit
517−
518− db <- IDBFactory.open dbName Nothing
519− { onUpgradeNeeded : Just callback
520− , onBlocked : Nothing
521− }
522− tearDown dbName dbVersion db
435+ tearDown db
523−
436+
524− it "count(Nothing) " do
437+ it "count()" do
525− let dbName = "db-count"
438+ { db } <- setup
526− dbVersion = 1
439+ { storeParams: IDBDatabase.defaultParameters
527− storeName = "store"
440+ , onUpgradeNeeded: Just $ \_ store -> launchAff' do
528− keyPath = []
529− autoIncrement = true
530− callback db = do
531− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
532− _ <- launchAff $ do
533− _ <- IDBObjectStore.add store "patate" Nothing
441+ _ <- IDBObjectStore.add store "patate" (Just 14)
534− _ <- IDBObjectStore.add store 14 Nothing
442+ _ <- IDBObjectStore.add store "autruche" (Just 42)
535 n <- IDBObjectStore.count store Nothing
443 n <- IDBObjectStore.count store Nothing
536 n `shouldEqual` 2
444 n `shouldEqual` 2
537− pure unit
538−
539− db <- IDBFactory.open dbName Nothing
540− { onUpgradeNeeded : Just callback
541− , onBlocked : Nothing
542− }
445+ }
543− tearDown dbName dbVersion db
446+ tearDown db
544−
447+
545−
546− it "getKey() (fixed)" do
448+ it "getKey()" do
547− let dbName = "db-getKey"
449+ { db } <- setup
548− dbVersion = 1
450+ { storeParams: IDBDatabase.defaultParameters
549− storeName = "store"
550− keyPath = ["id"]
551− key = "patate"
552− autoIncrement = false
553− callback db = do
554− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
555− _ <- launchAff $ do
451+ , onUpgradeNeeded: Just $ \_ store -> launchAff' do
556− key' <- IDBObjectStore.add store { id: key, value: 14 } Nothing
452+ key <- IDBObjectStore.add store "patate" (Just 14)
557− (unsafeFromKey key') `shouldEqual` key
558− key' <- IDBObjectStore.getKey store (IDBKeyRange.only key)
559− (unsafeFromKey <$> key') `shouldEqual` (Just key)
560− pure unit
561−
562− db <- IDBFactory.open dbName Nothing
563− { onUpgradeNeeded : Just callback
564− , onBlocked : Nothing
565− }
566− tearDown dbName dbVersion db
567−
568− it "getKey() (given)" do
569− let dbName = "db-getKey"
570− dbVersion = 1
571− storeName = "store"
572− keyPath = []
573− autoIncrement = true
574− callback db = do
575− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
576− _ <- launchAff $ do
577− key <- IDBObjectStore.add store "patate" Nothing
578− mkey <- IDBObjectStore.getKey store (IDBKeyRange.only key)
579− mkey `shouldEqual` Just key
580− pure unit
581−
582− db <- IDBFactory.open dbName Nothing
583− { onUpgradeNeeded : Just callback
584− , onBlocked : Nothing
585− }
586− tearDown dbName dbVersion db
587−
588− it "getKey() unknown" do
589− let dbName = "db-getKey"
590− dbVersion = 1
591− storeName = "store"
592− keyPath = []
593− autoIncrement = true
594− callback db = do
595− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
596− _ <- launchAff $ do
597− key <- IDBObjectStore.add store "patate" Nothing
598 mkey <- IDBObjectStore.getKey store (IDBKeyRange.only 14)
453 mkey <- IDBObjectStore.getKey store (IDBKeyRange.only 14)
599− mkey `shouldEqual` Nothing
454+ mkey `shouldEqual` (Just key)
600− pure unit
455+
601−
456+ mkey' <- IDBObjectStore.getKey store (IDBKeyRange.only 42)
602− db <- IDBFactory.open dbName Nothing
457+ mkey' `shouldEqual` none
603− { onUpgradeNeeded : Just callback
604− , onBlocked : Nothing
605− }
458+ }
606− tearDown dbName dbVersion db
459+ tearDown db
607−
460+
608− it "getAllKeys(nothing, nothing)" do
461+ it "getAllKeys()" do
609− let dbName = "db-getAllKeys"
462+ { db } <- setup
610− dbVersion = 1
463+ { storeParams: IDBDatabase.defaultParameters
611− storeName = "store"
612− keyPath = []
613− autoIncrement = true
614− callback db = do
615− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
616− _ <- launchAff $ do
464+ , onUpgradeNeeded: Just $ \_ store -> launchAff' do
617− k1 <- IDBObjectStore.add store "patate" Nothing
465+ key1 <- IDBObjectStore.add store "patate" (Just 14)
618− k2 <- IDBObjectStore.add store "autruche" Nothing
466+ key2 <- IDBObjectStore.add store "autruche" (Just 42)
467+ key3 <- IDBObjectStore.add store 14 (Just 1337)
468+
469+ -- no bounds
619 keys <- IDBObjectStore.getAllKeys store Nothing Nothing
470 keys <- IDBObjectStore.getAllKeys store Nothing Nothing
620− keys `shouldEqual` [k1,k2]
471+ keys `shouldEqual` [ key1, key2, key3 ]
621− pure unit
472+
622−
473+ -- lower bound
623− db <- IDBFactory.open dbName Nothing
474+ keys' <- IDBObjectStore.getAllKeys store (Just $ IDBKeyRange.lowerBound 14 true) Nothing
624− { onUpgradeNeeded : Just callback
475+ keys' `shouldEqual` [ key2, key3 ]
625− , onBlocked : Nothing
476+
626− }
477+ -- upper bound
627− tearDown dbName dbVersion db
628−
629− it "getAllKeys(lowerBound(1, true), nothing)" do
630− let dbName = "db-getAllKeys"
631− dbVersion = 1
632− storeName = "store"
633− keyPath = []
634− autoIncrement = true
635− callback db = do
636− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
637− _ <- launchAff $ do
638− k1 <- IDBObjectStore.add store "patate" Nothing
639− k2 <- IDBObjectStore.add store "autruche" Nothing
640− k3 <- IDBObjectStore.add store 14 Nothing
641− keys <- IDBObjectStore.getAllKeys store (Just $ IDBKeyRange.lowerBound 1 true) Nothing
478+ keys'' <- IDBObjectStore.getAllKeys store (Just $ IDBKeyRange.upperBound 42 false) Nothing
642− keys `shouldEqual` [k2, k3]
479+ keys'' `shouldEqual` [ key1, key2 ]
643− pure unit
480+
644−
481+ -- count
645− db <- IDBFactory.open dbName Nothing
482+ keys''' <- IDBObjectStore.getAllKeys store (Just $ IDBKeyRange.lowerBound 1 true) (Just 2)
646− { onUpgradeNeeded : Just callback
483+ keys''' `shouldEqual` [ key1, key2 ]
647− , onBlocked : Nothing
648− }
484+ }
649− tearDown dbName dbVersion db
485+ tearDown db
650−
486+
651− it "openCursor(Nothing, Next)" do
487+ it "openCursor()" do
652− let dbName = "db-openCursor"
488+ let
653− dbVersion = 1
489+ cb =
654− storeName = "store"
490+ { onSuccess: const $ pure unit
655− keyPath = []
491+ , onError: show >>> fail >>> launchAff'
656− autoIncrement = true
492+ , onComplete: pure unit
657− callback db = do
493+ }
658− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
494+ { db } <- setup
495+ { storeParams: IDBDatabase.defaultParameters
659− _ <- launchAff $ do
496+ , onUpgradeNeeded: Just $ \_ store -> launchAff' do
660− _ <- IDBObjectStore.openCursor store Nothing Next
497+ _ <- IDBObjectStore.openCursor store Nothing Next cb
661− pure unit
498+ _ <- IDBObjectStore.openCursor store Nothing NextUnique cb
662− pure unit
663−
664− db <- IDBFactory.open dbName Nothing
665− { onUpgradeNeeded : Just callback
666− , onBlocked : Nothing
667− }
668− tearDown dbName dbVersion db
669−
670− it "openCursor(Nothing, NextUnique)" do
671− let dbName = "db-openCursor"
672− dbVersion = 1
673− storeName = "store"
674− keyPath = []
675− autoIncrement = true
676− callback db = do
677− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
678− _ <- launchAff $ do
679− _ <- IDBObjectStore.openCursor store Nothing NextUnique
680− pure unit
681− pure unit
682−
683− db <- IDBFactory.open dbName Nothing
684− { onUpgradeNeeded : Just callback
685− , onBlocked : Nothing
686− }
687− tearDown dbName dbVersion db
688−
689− it "openCursor(Nothing, Prev)" do
690− let dbName = "db-openCursor"
691− dbVersion = 1
692− storeName = "store"
693− keyPath = []
694− autoIncrement = true
695− callback db = do
696− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
697− _ <- launchAff $ do
698− _ <- IDBObjectStore.openCursor store Nothing Prev
699− pure unit
700− pure unit
701−
702− db <- IDBFactory.open dbName Nothing
703− { onUpgradeNeeded : Just callback
704− , onBlocked : Nothing
705− }
706− tearDown dbName dbVersion db
707−
708− it "openCursor(Nothing, PrevUnique)" do
709− let dbName = "db-openCursor"
710− dbVersion = 1
711− storeName = "store"
712− keyPath = []
713− autoIncrement = true
714− callback db = do
715− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
716− _ <- launchAff $ do
717− _ <- IDBObjectStore.openCursor store Nothing PrevUnique
718− pure unit
719− pure unit
720−
721− db <- IDBFactory.open dbName Nothing
722− { onUpgradeNeeded : Just callback
723− , onBlocked : Nothing
724− }
725− tearDown dbName dbVersion db
726−
727− it "openCursor(Just upperBound, Next)" do
728− let dbName = "db-openCursor"
729− dbVersion = 1
730− storeName = "store"
731− keyPath = []
732− autoIncrement = true
733− callback db = do
734− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
735− _ <- launchAff $ do
736− _ <- IDBObjectStore.openCursor store (Just $ IDBKeyRange.upperBound 10 true) Next
737− pure unit
738− pure unit
739−
740− db <- IDBFactory.open dbName Nothing
741− { onUpgradeNeeded : Just callback
742− , onBlocked : Nothing
743− }
744− tearDown dbName dbVersion db
745−
746− it "openKeyCursor(Nothing, Next)" do
747− let dbName = "db-openKeyCursor"
748− dbVersion = 1
749− storeName = "store"
750− keyPath = []
751− autoIncrement = true
752− callback db = do
753− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
754− _ <- launchAff $ do
755− _ <- IDBObjectStore.openKeyCursor store Nothing Next
756− pure unit
757− pure unit
758−
759− db <- IDBFactory.open dbName Nothing
760− { onUpgradeNeeded : Just callback
761− , onBlocked : Nothing
762− }
763− tearDown dbName dbVersion db
764−
765− it "openKeyCursor(Nothing, NextUnique)" do
766− let dbName = "db-openKeyCursor"
767− dbVersion = 1
768− storeName = "store"
769− keyPath = []
770− autoIncrement = true
771− callback db = do
772− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
773− _ <- launchAff $ do
774− _ <- IDBObjectStore.openKeyCursor store Nothing NextUnique
775− pure unit
776− pure unit
777−
778− db <- IDBFactory.open dbName Nothing
779− { onUpgradeNeeded : Just callback
780− , onBlocked : Nothing
781− }
782− tearDown dbName dbVersion db
783−
784− it "openKeyCursor(Nothing, Prev)" do
785− let dbName = "db-openKeyCursor"
786− dbVersion = 1
787− storeName = "store"
788− keyPath = []
789− autoIncrement = true
790− callback db = do
791− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
792− _ <- launchAff $ do
793− _ <- IDBObjectStore.openKeyCursor store Nothing Prev
499+ _ <- IDBObjectStore.openCursor store Nothing Prev cb
794− pure unit
500+ _ <- IDBObjectStore.openCursor store Nothing PrevUnique cb
795− pure unit
501+ _ <- IDBObjectStore.openCursor store (Just $ IDBKeyRange.upperBound 1 true) Next cb
796−
797− db <- IDBFactory.open dbName Nothing
798− { onUpgradeNeeded : Just callback
799− , onBlocked : Nothing
800− }
801− tearDown dbName dbVersion db
802−
803− it "openKeyCursor(Nothing, PrevUnique)" do
804− let dbName = "db-openKeyCursor"
805− dbVersion = 1
806− storeName = "store"
807− keyPath = []
808− autoIncrement = true
809− callback db = do
810− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
811− _ <- launchAff $ do
812− _ <- IDBObjectStore.openKeyCursor store Nothing PrevUnique
813 pure unit
502 pure unit
814− pure unit
503+ }
815−
816− db <- IDBFactory.open dbName Nothing
817− { onUpgradeNeeded : Just callback
818− , onBlocked : Nothing
819− }
820− tearDown dbName dbVersion db
504+ tearDown db
821−
505+
822− it "openKeyCursor(Just upperBound, Next)" do
506+ it "openKeyCursor()" do
823− let dbName = "db-openKeyCursor"
507+ let
824− dbVersion = 1
508+ cb =
825− storeName = "store"
509+ { onSuccess: const $ pure unit
826− keyPath = []
510+ , onError: show >>> fail >>> launchAff'
827− autoIncrement = true
511+ , onComplete: pure unit
828− callback db = do
512+ }
829− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
513+ { db } <- setup
830− _ <- launchAff $ do
514+ { storeParams: IDBDatabase.defaultParameters
515+ , onUpgradeNeeded: Just $ \_ store -> launchAff' do
516+ _ <- IDBObjectStore.openKeyCursor store Nothing Next cb
517+ _ <- IDBObjectStore.openKeyCursor store Nothing NextUnique cb
518+ _ <- IDBObjectStore.openKeyCursor store Nothing Prev cb
519+ _ <- IDBObjectStore.openKeyCursor store Nothing PrevUnique cb
831− _ <- IDBObjectStore.openKeyCursor store (Just $ IDBKeyRange.upperBound 10 true) Next
520+ _ <- IDBObjectStore.openKeyCursor store (Just $ IDBKeyRange.lowerBound 1 true) Next cb
832 pure unit
521 pure unit
833− pure unit
522+ }
834−
523+ tearDown db
835− db <- IDBFactory.open dbName Nothing
524+
836− { onUpgradeNeeded : Just callback
525+ describe "IDBIndex" do
837− , onBlocked : Nothing
526+ let
838− }
527+ tearDown db = do
839− tearDown dbName dbVersion db
528+ IDBDatabase.close db
840−
529+ _ <- IDBFactory.deleteDatabase (IDBDatabase.name db)
841− it "store.autoIncrement[true]" do
530+ pure unit
842− let dbName = "db-autoIncrement"
531+
843− dbVersion = 1
532+ setup
844− storeName = "store"
533+ :: forall value
845− keyPath = []
534+ . { storeParams :: { keyPath :: Array String, autoIncrement :: Boolean }
846− autoIncrement = true
535+ , indexParams :: { unique :: Boolean, multiEntry :: Boolean }
847− callback db = do
536+ , values :: Array (Tuple value (Maybe Key))
848− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
537+ , keyPath :: Array String
849− let autoIncrement' = IDBObjectStore.autoIncrement store
538+ , onUpgradeNeeded :: Maybe (Database -> Transaction -> Index -> Effect Unit)
850− _ <- launchAff $ autoIncrement `shouldEqual` autoIncrement'
539+ }
851− pure unit
540+ -> Aff { db :: Database, index :: Index, store :: ObjectStore }
852−
541+ setup { storeParams, indexParams, values, keyPath, onUpgradeNeeded } = do
853− db <- IDBFactory.open dbName Nothing
542+ let
854− { onUpgradeNeeded : Just callback
543+ onUpgradeNeeded' var db tx _ = launchAff' do
855− , onBlocked : Nothing
544+ store <- IDBDatabase.createObjectStore db "store" storeParams
856− }
545+ _ <- traverse (uncurry (IDBObjectStore.add store)) values
857− tearDown dbName dbVersion db
546+ index <- IDBObjectStore.createIndex store "index" keyPath indexParams
858−
547+ liftEffect $ maybe (pure unit) identity (onUpgradeNeeded <*> pure db <*> pure tx <*> pure index)
859− it "store.autoIncrement[false]" do
548+ AVar.put { db, index, store } var
860− let dbName = "db-autoIncrement"
549+
861− dbVersion = 1
550+ var <- AVar.empty
862− storeName = "store"
551+ _ <- IDBFactory.open "db" Nothing
863− keyPath = []
552+ { onUpgradeNeeded: Just (onUpgradeNeeded' var)
864− autoIncrement = false
553+ , onBlocked: Nothing
865− callback db = do
554+ }
866− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
555+ AVar.take var
867− let autoIncrement' = IDBObjectStore.autoIncrement store
556+
868− _ <- launchAff $ autoIncrement `shouldEqual` autoIncrement'
557+ it "returns an IDBIndex and the properties are set correctly" do
869− pure unit
558+ { db, index } <- setup
870−
559+ { storeParams: IDBDatabase.defaultParameters
871− db <- IDBFactory.open dbName Nothing
560+ , indexParams: IDBObjectStore.defaultParameters
872− { onUpgradeNeeded : Just callback
561+ , onUpgradeNeeded: Nothing
873− , onBlocked : Nothing
562+ , keyPath: []
874− }
563+ , values: []
875− tearDown dbName dbVersion db
564+ }
876−
565+ IDBIndex.name index `shouldEqual` "index"
877− it "store.keyPath[\"patate\"]" do
566+ IDBIndex.keyPath index `shouldEqual` []
878− let dbName = "db-keyPath"
567+ IDBIndex.unique index `shouldEqual` IDBObjectStore.defaultParameters.unique
879− dbVersion = 1
568+ IDBIndex.multiEntry index `shouldEqual` IDBObjectStore.defaultParameters.multiEntry
880− storeName = "store"
569+ tearDown db
881− keyPath = ["patate"]
570+
882− autoIncrement = false
571+ it "attempt to create an index that requires unique values on an object store already contains duplicates" do
883− callback db = do
572+ let onAbort var = launchAff' (AVar.put true var)
884− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
573+ txVar <- AVar.empty
885− let keyPath' = IDBObjectStore.keyPath store
574+ dbVar <- AVar.empty
886− _ <- launchAff $ keyPath `shouldEqual` keyPath'
575+ res <- attempt $ setup
887− pure unit
576+ { storeParams: IDBDatabase.defaultParameters
888−
577+ , indexParams:
889− db <- IDBFactory.open dbName Nothing
578+ { unique: true
890− { onUpgradeNeeded : Just callback
579+ , multiEntry: false
891− , onBlocked : Nothing
580+ }
892− }
581+ , keyPath: [ "indexedProperty" ]
893− tearDown dbName dbVersion db
582+ , values:
894−
583+ [ { indexedProperty: "bar" } :+: (Just $ toKey 1)
895− it "store.indexNames[]" do
584+ , { indexedProperty: "bar" } :+: (Just $ toKey 2)
896− let dbName = "db-indexNames"
585+ ]
897− dbVersion = 1
586+ , onUpgradeNeeded: Just $ \db tx _ -> launchAff' do
898− storeName = "store"
587+ IDBTransaction.onAbort tx (onAbort txVar)
899− keyPath = []
588+ IDBDatabase.onAbort db (onAbort dbVar)
900− autoIncrement = false
589+ }
901− callback db = do
590+ case res of
902− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
591+ Right _ ->
903− let indexNames = IDBObjectStore.indexNames store
592+ fail "expected abort"
904− _ <- launchAff $ indexNames `shouldEqual` []
593+ _ -> do
905− pure unit
594+ shouldEqual true =<< AVar.take txVar
906−
595+ shouldEqual true =<< AVar.take dbVar
907− db <- IDBFactory.open dbName Nothing
596+
908− { onUpgradeNeeded : Just callback
597+ it "the index is usable right after being made" do
909− , onBlocked : Nothing
598+ { db } <- setup
910− }
599+ { storeParams:
911− tearDown dbName dbVersion db
600+ { keyPath: [ "key" ]
912−
601+ , autoIncrement: false
913− it "store.name[\"store\"]" do
602+ }
914− let dbName = "db-indexNames"
603+ , indexParams: IDBObjectStore.defaultParameters
915− dbVersion = 1
604+ , keyPath: [ "indexedProperty" ]
916− storeName = "store"
605+ , values:
917− keyPath = []
606+ [ { key: "key1", indexedProperty: "indexed_1" } :+: Nothing
918− autoIncrement = false
607+ , { key: "key2", indexedProperty: "indexed_2" } :+: Nothing
919− callback db = do
608+ , { key: "key3", indexedProperty: "indexed_3" } :+: Nothing
920− store <- IDBDatabase.createObjectStore db storeName { keyPath, autoIncrement }
609+ ]
921− let storeName' = IDBObjectStore.name store
610+ , onUpgradeNeeded: Just $ \_ _ index -> launchAff' do
922− _ <- launchAff $ storeName `shouldEqual` storeName'
611+ val <- IDBIndex.get index (IDBKeyRange.only "indexed_2")
923− pure unit
612+ ((\r -> r.key) <$> val) `shouldEqual` (Just $ toKey "key2")
613+ }
614+ tearDown db
615+
616+ it "empty keyPath" do
617+ { db } <- setup
618+ { storeParams: IDBDatabase.defaultParameters
619+ , indexParams: IDBObjectStore.defaultParameters
620+ , keyPath: []
621+ , values:
622+ [ "object_1" :+: (Just $ toKey 1)
623+ , "object_2" :+: (Just $ toKey 2)
624+ , "object_3" :+: (Just $ toKey 3)
625+ ]
626+ , onUpgradeNeeded: Just $ \_ _ index -> launchAff' do
627+ val <- IDBIndex.get index (IDBKeyRange.only "object_3")
628+ val `shouldEqual` (Just "object_3")
629+ }
630+ tearDown db
631+
632+ it "index can be valid keys [date]" do
633+ date <- liftEffect $ toDateTime <$> now
634+ { db } <- setup
635+ { storeParams:
636+ { keyPath: [ "key" ]
637+ , autoIncrement: false
638+ }
639+ , indexParams: IDBObjectStore.defaultParameters
640+ , keyPath: [ "i" ]
641+ , values:
642+ [ { key: "date", i: (toKey date) } :+: Nothing
643+ ]
644+ , onUpgradeNeeded: Just $ \_ _ index -> launchAff' do
645+ val <- IDBIndex.get index (IDBKeyRange.only date)
646+ ((\r -> r.key) <$> val) `shouldEqual` (Just "date")
647+ }
648+ tearDown db
649+
650+ it "index can be valid keys [num]" do
651+ let num = 14
652+ { db } <- setup
653+ { storeParams:
654+ { keyPath: [ "key" ]
655+ , autoIncrement: false
656+ }
657+ , indexParams: IDBObjectStore.defaultParameters
658+ , keyPath: [ "i" ]
659+ , values:
660+ [ { key: "num", i: (toKey num) } :+: Nothing
661+ ]
662+ , onUpgradeNeeded: Just $ \_ _ index -> launchAff' do
663+ val <- IDBIndex.get index (IDBKeyRange.only num)
664+ ((\r -> r.key) <$> val) `shouldEqual` (Just "num")
665+ }
666+ tearDown db
667+
668+ it "index can be valid keys [array]" do
669+ let array = [ "patate", "autruche" ]
670+ { db } <- setup
671+ { storeParams:
672+ { keyPath: [ "key" ]
673+ , autoIncrement: false
674+ }
675+ , indexParams: IDBObjectStore.defaultParameters
676+ , keyPath: [ "i" ]
677+ , values:
678+ [ { key: "array", i: (toKey array) } :+: Nothing
679+ ]
680+ , onUpgradeNeeded: Just $ \_ _ index -> launchAff' do
681+ val <- IDBIndex.get index (IDBKeyRange.only array)
682+ ((\r -> r.key) <$> val) `shouldEqual` (Just "array")
683+ }
684+ tearDown db
685+
686+ it "openKeyCursor() - throw InvalidStateError on index deleted by aborted upgrade" do
687+ res <- attempt $ setup
688+ { storeParams: { keyPath: [ "key" ], autoIncrement: false }
689+ , indexParams: IDBObjectStore.defaultParameters
690+ , keyPath: [ "indexedProperty" ]
691+ , values:
692+ [ { key: 14, indexedProperty: "patate" } :+: Nothing
693+ ]
694+ , onUpgradeNeeded: Just $ \db tx index -> launchAff' do
695+ let
696+ cb =
697+ { onSuccess: const $ pure unit
698+ , onError: show >>> fail >>> launchAff'
699+ , onComplete: pure unit
700+ }
701+ IDBTransaction.onAbort tx (pure unit)
702+ IDBDatabase.onAbort db (pure unit)
703+ IDBTransaction.abort tx
704+ cursor <- attempt $ IDBIndex.openKeyCursor index Nothing Next cb
705+ case cursor of
706+ Right _ -> fail "expected InvalidStateError"
707+ Left err -> name err `shouldEqual` "InvalidStateError"
708+ }
709+ case res of
710+ Right _ -> fail "expected InvalidStateError"
711+ _ -> pure unit
712+
713+ it "openKeyCursor() - throw TransactionInactiveError on aborted transaction" do
714+ { db } <- setup
715+ { storeParams: { keyPath: [ "key" ], autoIncrement: false }
716+ , indexParams: IDBObjectStore.defaultParameters
717+ , keyPath: [ "indexedProperty" ]
718+ , values:
719+ [ { key: 14, indexedProperty: "patate" } :+: Nothing
720+ ]
721+ , onUpgradeNeeded: Nothing
722+ }
723+ let
724+ cb =
725+ { onSuccess: const $ pure unit
726+ , onError: show >>> fail >>> launchAff'
727+ , onComplete: pure unit
728+ }
729+ tx <- IDBDatabase.transaction db [ "store" ] ReadOnly
730+ store <- IDBTransaction.objectStore tx "store"
731+ index <- IDBObjectStore.index store "index"
732+ IDBTransaction.onAbort tx (pure unit)
733+ IDBTransaction.abort tx
734+ cursor <- attempt $ IDBIndex.openKeyCursor index Nothing Next cb
735+ case cursor of
736+ Right _ -> fail "expected TransactionInactiveError"
737+ Left err -> name err `shouldEqual` "TransactionInactiveError"
738+
739+ tearDown db
740+
741+ it "openKeyCursor() - throw InvalidStateError when the index is deleted" do
742+ { db } <- setup
743+ { storeParams: { keyPath: [ "key" ], autoIncrement: false }
744+ , indexParams: IDBObjectStore.defaultParameters
745+ , keyPath: [ "indexedProperty" ]
746+ , values:
747+ [ { key: 14, indexedProperty: "patate" } :+: Nothing
748+ ]
749+ , onUpgradeNeeded: Just $ \_ tx index -> launchAff' do
750+ let
751+ cb =
752+ { onSuccess: const $ pure unit
753+ , onError: show >>> fail >>> launchAff'
754+ , onComplete: pure unit
755+ }
756+ store <- IDBTransaction.objectStore tx "store"
757+ IDBObjectStore.deleteIndex store "index"
758+ cursor <- attempt $ IDBIndex.openKeyCursor index Nothing Next cb
759+ case cursor of
760+ Right _ -> fail "expected InvalidStateError"
761+ Left err -> name err `shouldEqual` "InvalidStateError"
762+ }
763+
764+ tearDown db
765+
766+ it "openCursor() - throw InvalidStateError on index deleted by aborted upgrade" do
767+ res <- attempt $ setup
768+ { storeParams: { keyPath: [ "key" ], autoIncrement: false }
769+ , indexParams: IDBObjectStore.defaultParameters
770+ , keyPath: [ "indexedProperty" ]
771+ , values:
772+ [ { key: 14, indexedProperty: "patate" } :+: Nothing
773+ ]
774+ , onUpgradeNeeded: Just $ \db tx index -> launchAff' do
775+ let
776+ cb =
777+ { onSuccess: const $ pure unit
778+ , onError: show >>> fail >>> launchAff'
779+ , onComplete: pure unit
780+ }
781+ IDBTransaction.onAbort tx (pure unit)
782+ IDBDatabase.onAbort db (pure unit)
783+ IDBTransaction.abort tx
784+ cursor <- attempt $ IDBIndex.openCursor index Nothing Next cb
785+ case cursor of
786+ Right _ -> fail "expected InvalidStateError"
787+ Left err -> name err `shouldEqual` "InvalidStateError"
788+ }
789+ case res of
790+ Right _ -> fail "expected InvalidStateError"
791+ _ -> pure unit
792+
793+ it "openCursor() - throw TransactionInactiveError on aborted transaction" do
794+ { db } <- setup
795+ { storeParams: { keyPath: [ "key" ], autoIncrement: false }
796+ , indexParams: IDBObjectStore.defaultParameters
797+ , keyPath: [ "indexedProperty" ]
798+ , values:
799+ [ { key: 14, indexedProperty: "patate" } :+: Nothing
800+ ]
801+ , onUpgradeNeeded: Nothing
802+ }
803+ let
804+ cb =
805+ { onSuccess: const $ pure unit
806+ , onError: show >>> fail >>> launchAff'
807+ , onComplete: pure unit
808+ }
809+ tx <- IDBDatabase.transaction db [ "store" ] ReadOnly
810+ store <- IDBTransaction.objectStore tx "store"
811+ index <- IDBObjectStore.index store "index"
812+ IDBTransaction.onAbort tx (pure unit)
813+ IDBTransaction.abort tx
814+ cursor <- attempt $ IDBIndex.openCursor index Nothing Next cb
815+ case cursor of
816+ Right _ -> fail "expected TransactionInactiveError"
817+ Left err -> name err `shouldEqual` "TransactionInactiveError"
818+
819+ tearDown db
820+
821+ it "openCursor() - throw InvalidStateError when the index is deleted" do
822+ { db } <- setup
823+ { storeParams: { keyPath: [ "key" ], autoIncrement: false }
824+ , indexParams: IDBObjectStore.defaultParameters
825+ , keyPath: [ "indexedProperty" ]
826+ , values:
827+ [ { key: 14, indexedProperty: "patate" } :+: Nothing
828+ ]
829+ , onUpgradeNeeded: Just $ \db tx index -> launchAff' do
830+ let
831+ cb =
832+ { onSuccess: const $ pure unit
833+ , onError: show >>> fail >>> launchAff'
834+ , onComplete: pure unit
835+ }
836+ store <- IDBTransaction.objectStore tx "store"
837+ IDBObjectStore.deleteIndex store "index"
838+ cursor <- attempt $ IDBIndex.openCursor index Nothing Next cb
839+ case cursor of
840+ Right _ -> fail "expected InvalidStateError"
841+ Left err -> name err `shouldEqual` "InvalidStateError"
842+ }
843+
844+ tearDown db
845+
846+ it "getKey() - multiEntry - adding keys" do
847+ { db } <- setup
848+ { storeParams: IDBDatabase.defaultParameters
849+ , indexParams: { unique: false, multiEntry: true }
850+ , keyPath: [ "name" ]
851+ , values:
852+ [ { name: [ "patate", "autruche" ] } :+: (Just $ toKey 1)
853+ , { name: [ "bob" ] } :+: (Just $ toKey 2)
854+ ]
855+ , onUpgradeNeeded: Just $ \_ _ index -> launchAff' do
856+ key <- IDBIndex.getKey index (IDBKeyRange.only "patate")
857+ key `shouldEqual` (Just $ toKey 1)
858+
859+ key' <- IDBIndex.getKey index (IDBKeyRange.only "autruche")
860+ key' `shouldEqual` (Just $ toKey 1)
861+
862+ key'' <- IDBIndex.getKey index (IDBKeyRange.only "bob")
863+ key'' `shouldEqual` (Just $ toKey 2)
864+ }
865+ tearDown db
866+
867+ it "get() - returns the record with the first key in the range" do
868+ { db } <- setup
869+ { storeParams: { keyPath: [ "key" ], autoIncrement: false }
870+ , indexParams: IDBObjectStore.defaultParameters
871+ , keyPath: [ "indexedProperty" ]
872+ , values:
873+ [ { key: 14, indexedProperty: "patate" } :+: Nothing
874+ , { key: 42, indexedProperty: "autruche" } :+: Nothing
875+ , { key: 1337, indexedProperty: "baguette" } :+: Nothing
876+ ]
877+ , onUpgradeNeeded: Nothing
878+ }
879+
880+ tx <- IDBDatabase.transaction db [ "store" ] ReadOnly
881+ store <- IDBTransaction.objectStore tx "store"
882+ index <- IDBObjectStore.index store "index"
883+ val <- IDBIndex.get index (IDBKeyRange.lowerBound "autruche" false)
884+ ((\r -> r.key) <$> val) `shouldEqual` (Just 42)
885+
886+ tearDown db
887+
888+ describe "IDBCursor" do
889+ let
890+ tearDown db = do
891+ IDBDatabase.close db
892+ _ <- IDBFactory.deleteDatabase (IDBDatabase.name db)
893+ pure unit
894+
895+ setup
896+ :: forall value
897+ . { storeParams :: { keyPath :: Array String, autoIncrement :: Boolean }
898+ , indexParams :: { unique :: Boolean, multiEntry :: Boolean }
899+ , values :: Array (Tuple value (Maybe Key))
900+ , keyPath :: Array String
901+ , onUpgradeNeeded :: Maybe (Database -> Transaction -> Index -> Effect Unit)
902+ }
903+ -> Aff { db :: Database, index :: Index, store :: ObjectStore }
904+ setup { storeParams, indexParams, values, keyPath, onUpgradeNeeded } = do
905+ let
906+ onUpgradeNeeded' var db tx _ = launchAff' do
907+ store <- IDBDatabase.createObjectStore db "store" storeParams
908+ _ <- traverse (uncurry (IDBObjectStore.add store)) values
909+ index <- IDBObjectStore.createIndex store "index" keyPath indexParams
910+ liftEffect $ maybe (pure unit) identity (onUpgradeNeeded <*> pure db <*> pure tx <*> pure index)
911+ AVar.put { db, index, store } var
912+
913+ var <- AVar.empty
914+ db <- IDBFactory.open "db" Nothing
915+ { onUpgradeNeeded: Just (onUpgradeNeeded' var)
916+ , onBlocked: Nothing
917+ }
918+ AVar.take var
919+
920+ it "continue() - iterate to the next record" do
921+ { db } <- setup
922+ { storeParams: IDBDatabase.defaultParameters
923+ , indexParams: IDBObjectStore.defaultParameters
924+ , keyPath: []
925+ , values:
926+ [ "cupcake" :+: (Just $ toKey 4)
927+ , "pancake" :+: (Just $ toKey 2)
928+ , "pie" :+: (Just $ toKey 1)
929+ , "pie" :+: (Just $ toKey 3)
930+ ]
931+ , onUpgradeNeeded: Nothing
932+ }
933+ let
934+ cb vdone vvals =
935+ { onComplete: launchAff' do
936+ AVar.put unit vdone
937+
938+ , onError: \error -> launchAff' do
939+ fail $ "unexpected error: " <> show error
940+
941+ , onSuccess: \cursor -> launchAff' do
942+ vals <- AVar.take vvals
943+ pure (IDBCursor.value cursor) >>= shouldEqual (maybe "" _.v $ head vals)
944+ IDBCursor.primaryKey cursor >>= shouldEqual (maybe (toKey 0) _.k $ head vals)
945+ AVar.put (drop 1 vals) vvals
946+ IDBCursor.continue cursor none
947+ }
948+ vdone <- AVar.empty
949+ vvals <- AVar.new
950+ [ { v: "pie", k: toKey 1 }
951+ , { v: "pancake", k: toKey 2 }
952+ , { v: "pie", k: toKey 3 }
953+ , { v: "cupcake", k: toKey 4 }
954+ ]
955+ tx <- IDBDatabase.transaction db [ "store" ] ReadOnly
956+ store <- IDBTransaction.objectStore tx "store"
957+ IDBObjectStore.openCursor store Nothing Next (cb vdone vvals)
958+ AVar.take vdone
959+ tearDown db
960+
961+ it "continue() - attempt to iterate in the wrong direction" do
962+ { db } <- setup
963+ { storeParams: IDBDatabase.defaultParameters
964+ , indexParams: IDBObjectStore.defaultParameters
965+ , keyPath: []
966+ , values:
967+ [ "cupcake" :+: (Just $ toKey 4)
968+ , "pancake" :+: (Just $ toKey 2)
969+ , "pie" :+: (Just $ toKey 1)
970+ , "pie" :+: (Just $ toKey 3)
971+ ]
972+ , onUpgradeNeeded: Nothing
973+ }
974+ let
975+ cb vdone =
976+ { onComplete: launchAff' do
977+ fail $ "shouldn't complete"
978+
979+ , onError: \error -> launchAff' do
980+ fail $ "unexpected error: " <> show error
981+
982+ , onSuccess: \cursor -> launchAff' do
983+ res <- attempt $ IDBCursor.continue cursor (Just 1)
984+ case res of
985+ Left err -> do
986+ name err `shouldEqual` "DataError"
987+ AVar.put unit vdone
988+ Right _ -> do
989+ fail "expected continue to fail"
990+ }
991+ vdone <- AVar.empty
992+ tx <- IDBDatabase.transaction db [ "store" ] ReadOnly
993+ store <- IDBTransaction.objectStore tx "store"
994+ IDBObjectStore.openCursor store Nothing Next (cb vdone)
995+ AVar.take vdone
996+ tearDown db
997+
998+ it "advance() - iterate cursor number of times specified by count" do
999+ { db } <- setup
1000+ { storeParams: { keyPath: [ "pKey" ], autoIncrement: false }
1001+ , indexParams: IDBObjectStore.defaultParameters
1002+ , keyPath: []
1003+ , values:
1004+ [ { pKey: "pkey_0", iKey: "ikey_0" } :+: Nothing
1005+ , { pKey: "pkey_1", iKey: "ikey_1" } :+: Nothing
1006+ , { pKey: "pkey_2", iKey: "ikey_2" } :+: Nothing
1007+ , { pKey: "pkey_3", iKey: "ikey_3" } :+: Nothing
1008+ ]
1009+ , onUpgradeNeeded: Nothing
1010+ }
1011+ let
1012+ cb vdone vjump =
1013+ { onComplete: launchAff' do
1014+ AVar.put unit vdone
1015+
1016+ , onError: \error -> launchAff' do
1017+ fail $ "unexpected error: " <> show error
1018+
1019+ , onSuccess: \cursor -> launchAff' do
1020+ jump <- AVar.take vjump
1021+ if jump then do
1022+ IDBCursor.advance cursor 3
1023+ else do
1024+ let value = IDBCursor.value cursor
1025+ value.pKey `shouldEqual` "pkey_3"
1026+ value.iKey `shouldEqual` "ikey_3"
1027+ IDBCursor.continue cursor none
1028+ AVar.put false vjump
1029+ }
1030+ vdone <- AVar.empty
1031+ vjump <- AVar.new true
1032+ tx <- IDBDatabase.transaction db [ "store" ] ReadOnly
1033+ store <- IDBTransaction.objectStore tx "store"
1034+ IDBObjectStore.openCursor store Nothing Next (cb vdone vjump)
1035+ AVar.take vdone
1036+ tearDown db
1037+
1038+ it "delete() - remove a record from the object store" do
1039+ { db } <- setup
1040+ { storeParams: { keyPath: [ "pKey" ], autoIncrement: false }
1041+ , indexParams: IDBObjectStore.defaultParameters
1042+ , keyPath: []
1043+ , values:
1044+ [ { pKey: "pkey_0", iKey: "ikey_0" } :+: Nothing
1045+ , { pKey: "pkey_1", iKey: "ikey_1" } :+: Nothing
1046+ , { pKey: "pkey_2", iKey: "ikey_2" } :+: Nothing
1047+ , { pKey: "pkey_3", iKey: "ikey_3" } :+: Nothing
1048+ ]
1049+ , onUpgradeNeeded: Nothing
1050+ }
1051+ let
1052+ cb vdone store =
1053+ { onComplete: launchAff' do
1054+ mval <- map _.pKey <$> IDBIndex.get store (IDBKeyRange.only "pkey_0")
1055+ mval `shouldEqual` (Nothing :: Maybe String)
1056+ AVar.put unit vdone
1057+
1058+ , onError: \error -> launchAff' do
1059+ fail $ "unexpected error: " <> show error
1060+
1061+ , onSuccess: \cursor -> launchAff' do
1062+ let value = IDBCursor.value cursor
1063+ value.pKey `shouldEqual` "pkey_0"
1064+ IDBCursor.delete cursor
1065+ IDBCursor.advance cursor 4
1066+ }
1067+ vdone <- AVar.empty
1068+ tx <- IDBDatabase.transaction db [ "store" ] ReadWrite
1069+ store <- IDBTransaction.objectStore tx "store"
1070+ IDBObjectStore.openCursor store Nothing Next (cb vdone store)
1071+ AVar.take vdone
1072+ tearDown db
1073+
1074+ it "update() - modify a record in the object store" do
1075+ { db } <- setup
1076+ { storeParams: { keyPath: [ "pKey" ], autoIncrement: false }
1077+ , indexParams: IDBObjectStore.defaultParameters
1078+ , keyPath: []
1079+ , values:
1080+ [ { pKey: "pkey_0", iKey: "ikey_0" } :+: Nothing
1081+ ]
1082+ , onUpgradeNeeded: Nothing
1083+ }
1084+ let
1085+ cb vdone store =
1086+ { onComplete: launchAff' do
1087+ mval <- map _.iKey <$> IDBIndex.get store (IDBKeyRange.only "pkey_0")
1088+ mval `shouldEqual` (Just "patate")
1089+ AVar.put unit vdone
1090+
1091+ , onError: \error -> launchAff' do
1092+ fail $ "unexpected error: " <> show error
1093+
1094+ , onSuccess: \cursor -> launchAff' do
1095+ let value = IDBCursor.value cursor
1096+ value.pKey `shouldEqual` "pkey_0"
1097+ key <- IDBCursor.update cursor { pKey: "pkey_0", iKey: "patate" }
1098+ key `shouldEqual` toKey "pkey_0"
1099+ IDBCursor.advance cursor 4
1100+ }
1101+ vdone <- AVar.empty
1102+ tx <- IDBDatabase.transaction db [ "store" ] ReadWrite
1103+ store <- IDBTransaction.objectStore tx "store"
1104+ IDBObjectStore.openCursor store Nothing Next (cb vdone store)
1105+ AVar.take vdone
1106+ tearDown db
1107+
1108+ it "update() - throw ReadOnlyError after update on ReadOnly transaction" do
1109+ { db } <- setup
1110+ { storeParams: { keyPath: [ "pKey" ], autoIncrement: false }
1111+ , indexParams: IDBObjectStore.defaultParameters
1112+ , keyPath: []
1113+ , values:
1114+ [ { pKey: "pkey_0", iKey: "ikey_0" } :+: Nothing
1115+ ]
1116+ , onUpgradeNeeded: Nothing
1117+ }
1118+ let
1119+ cb vdone =
1120+ { onComplete: launchAff' do
1121+ fail $ "shouldn't complete"
1122+
1123+ , onError: \error -> launchAff' do
1124+ fail $ "unexpected error: " <> show error
1125+
1126+ , onSuccess: \cursor -> launchAff' do
1127+ res <- attempt $ IDBCursor.update cursor "patate"
1128+ case res of
1129+ Left err -> do
1130+ name err `shouldEqual` "ReadOnlyError"
1131+ AVar.put unit vdone
1132+ Right _ ->
1133+ fail $ "expected ReadOnlyError"
1134+ }
1135+ vdone <- AVar.empty
1136+ tx <- IDBDatabase.transaction db [ "store" ] ReadOnly
1137+ store <- IDBTransaction.objectStore tx "store"
1138+ IDBObjectStore.openCursor store Nothing Next (cb vdone)
1139+ AVar.take vdone
1140+ tearDown db
924
1141
925− db <- IDBFactory.open dbName Nothing
926− { onUpgradeNeeded : Just callback
927− , onBlocked : Nothing
928− }
929− tearDown dbName dbVersion db
yarn.locknew file+81
@@ -0,0 +1,81 @@
1+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2+# yarn lockfile v1
3+
4+
5+base64-arraybuffer-es6@^0.7.0:
6+ version "0.7.0"
7+ resolved "https://registry.yarnpkg.com/base64-arraybuffer-es6/-/base64-arraybuffer-es6-0.7.0.tgz#dbe1e6c87b1bf1ca2875904461a7de40f21abc86"
8+ integrity sha512-ESyU/U1CFZDJUdr+neHRhNozeCv72Y7Vm0m1DCbjX3KBjT6eYocvAJlSk6+8+HkVwXlT1FNxhGW6q3UKAlCvvw==
9+
10+domexception@^1.0.1:
11+ version "1.0.1"
12+ resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90"
13+ integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==
14+ dependencies:
15+ webidl-conversions "^4.0.2"
16+
17+fake-indexeddb@^4.0.1:
18+ version "4.0.1"
19+ resolved "https://registry.yarnpkg.com/fake-indexeddb/-/fake-indexeddb-4.0.1.tgz#09bb2468e21d0832b2177e894765fb109edac8fb"
20+ integrity sha512-hFRyPmvEZILYgdcLBxVdHLik4Tj3gDTu/g7s9ZDOiU3sTNiGx+vEu1ri/AMsFJUZ/1sdRbAVrEcKndh3sViBcA==
21+ dependencies:
22+ realistic-structured-clone "^3.0.0"
23+
24+lodash@^4.7.0:
25+ version "4.17.21"
26+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
27+ integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
28+
29+punycode@^2.1.1:
30+ version "2.3.0"
31+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f"
32+ integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==
33+
34+realistic-structured-clone@^3.0.0:
35+ version "3.0.0"
36+ resolved "https://registry.yarnpkg.com/realistic-structured-clone/-/realistic-structured-clone-3.0.0.tgz#7b518049ce2dad41ac32b421cd297075b00e3e35"
37+ integrity sha512-rOjh4nuWkAqf9PWu6JVpOWD4ndI+JHfgiZeMmujYcPi+fvILUu7g6l26TC1K5aBIp34nV+jE1cDO75EKOfHC5Q==
38+ dependencies:
39+ domexception "^1.0.1"
40+ typeson "^6.1.0"
41+ typeson-registry "^1.0.0-alpha.20"
42+
43+tr46@^2.1.0:
44+ version "2.1.0"
45+ resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240"
46+ integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==
47+ dependencies:
48+ punycode "^2.1.1"
49+
50+typeson-registry@^1.0.0-alpha.20:
51+ version "1.0.0-alpha.39"
52+ resolved "https://registry.yarnpkg.com/typeson-registry/-/typeson-registry-1.0.0-alpha.39.tgz#9e0f5aabd5eebfcffd65a796487541196f4b1211"
53+ integrity sha512-NeGDEquhw+yfwNhguLPcZ9Oj0fzbADiX4R0WxvoY8nGhy98IbzQy1sezjoEFWOywOboj/DWehI+/aUlRVrJnnw==
54+ dependencies:
55+ base64-arraybuffer-es6 "^0.7.0"
56+ typeson "^6.0.0"
57+ whatwg-url "^8.4.0"
58+
59+typeson@^6.0.0, typeson@^6.1.0:
60+ version "6.1.0"
61+ resolved "https://registry.yarnpkg.com/typeson/-/typeson-6.1.0.tgz#5b2a53705a5f58ff4d6f82f965917cabd0d7448b"
62+ integrity sha512-6FTtyGr8ldU0pfbvW/eOZrEtEkczHRUtduBnA90Jh9kMPCiFNnXIon3vF41N0S4tV1HHQt4Hk1j4srpESziCaA==
63+
64+webidl-conversions@^4.0.2:
65+ version "4.0.2"
66+ resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
67+ integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
68+
69+webidl-conversions@^6.1.0:
70+ version "6.1.0"
71+ resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514"
72+ integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==
73+
74+whatwg-url@^8.4.0:
75+ version "8.7.0"
76+ resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77"
77+ integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==
78+ dependencies:
79+ lodash "^4.7.0"
80+ tr46 "^2.1.0"
81+ webidl-conversions "^6.1.0"