git.iko.soy/purescript-indexed-DB
on master help sign in
35 files changed+3173−2113
since splitsnapshots
unifiedsplit
.github/browserstack.png .gitignore+3−1 .travis.yml+37 README.md+75−251 bower.json−19 index.js+4 karma.conf.js−25 package.json+8−39 packages.dhall+105 spago.dhall+42 src/Database/IndexedDB/Core.js+6−6 src/Database/IndexedDB/Core.purs+165−56 src/Database/IndexedDB/IDBCursor.js+75−25 src/Database/IndexedDB/IDBCursor.purs+154−106 src/Database/IndexedDB/IDBDatabase.js+62−28 src/Database/IndexedDB/IDBDatabase.purs+160−75 src/Database/IndexedDB/IDBFactory.js+21−8 src/Database/IndexedDB/IDBFactory.purs+61−21 src/Database/IndexedDB/IDBIndex.js+171 src/Database/IndexedDB/IDBIndex.purs+199−7 src/Database/IndexedDB/IDBIndex/Internal.js−129 src/Database/IndexedDB/IDBIndex/Internal.purs−145 src/Database/IndexedDB/IDBKey.purs+11−1 src/Database/IndexedDB/IDBKey/Internal.js+3−3 src/Database/IndexedDB/IDBKey/Internal.purs+114−99 src/Database/IndexedDB/IDBKeyRange.js+9−9 src/Database/IndexedDB/IDBKeyRange.purs+120−60 src/Database/IndexedDB/IDBObjectStore.js+55−28 src/Database/IndexedDB/IDBObjectStore.purs+217−107 src/Database/IndexedDB/IDBTransaction.js+44−16 src/Database/IndexedDB/IDBTransaction.purs+111−52 src/Test/Spec/Mocha.js−46 src/Test/Spec/Mocha.purs−41 test/Main.purs+1060−710 yarn.lock+81
.github/browserstack.pngnew file

Binary file not shown.

.gitignore+3−1
@@ -71,9 +71,11 @@ typings/
7171# Generated files
7272output
7373generated-docs
74+releases
7475.pulp-cache
7576.psc*
7677.purs*
7778.psa*
7879dist
79−src/Main.purs
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+=====
23
3−This document is a general grooming of the current state of the IndexedDB API. It covers only
4−the features specified in the official specs.
4+This package offers complete bindings and type-safety upon the [IndexedDB API](https://w3c.github.io/IndexedDB).
55
6−### Remarks
6+## Overview
77
8−- Better define a 'all' range instead of using Maybe `IDBKeyRange` in function
9−signatures (Nothing meaning 'all').
8+The `IDBCore` and `IDBFactory` are the two entry points required to create and connect to an
9+indexed database. From there, modules are divided such that each of them covers a specific IDB
10+interface.
1011
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` ...)
1214
13−- The `IDBRequest` is mostly a callback result holding an error or a value. We probably want to
14− use a typed Except or ExceptT instead.
15+Here's a quick example of what it look likes.
16+```purescript
17+module Main where
1518
19+import Prelude
1620
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)
1826
19−- [IDBCursor](#idbcursor)
20−- [IDBCursorWithValue](#idbcursorwithvalue)
21−- [IDBCursorDirection](#idbcursordirection)
22−- [IDBDatabase](#idbdatabase)
23−- [IDBError](#idberror)
24−- [IDBFactory](#idbfactory)
25−- [IDBIndex](#idbindex)
26−- [IDBKeyRange](#idbkeyrange)
27−- [IDBRequest](#idbrequest)
28−- [IDBOpenDBRequest](#idbopendbrequest)
29−- [IDBObjectStore](#idbobjectstore)
30−- [IDBTransaction](#idbtransaction)
31−- [IDBTransactionMode](#idbtransactionmode)
27+import Database.IndexedDB.Core
28+import Database.IndexedDB.IDBFactory as IDBFactory
29+import Database.IndexedDB.IDBDatabase as IDBDatabase
30+import Database.IndexedDB.IDBObjectStore as IDBObjectStore
31+import Database.IndexedDB.IDBIndex as IDBIndex
32+import Database.IndexedDB.IDBTransaction as IDBTransaction
33+import Database.IndexedDB.IDBKeyRange as IDBKeyRange
3234
3335
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+ }
3541
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
3646
37−#### IDBCursor
38−> https://developer.mozilla.org/en-US/docs/Web/API/IDBCursor
3947
40−- source :: [IDBIndex](#idbindex), [IDBObjectStore](#idbobjectstore)
41−- direction :: [IDBCursorDirection](#idbcursordirection)
42−- key :: [IDBKey](#idbkey)
43−- primaryKey :: [IDBKey](#idbkey)
44−- value :: Any
45−- advance :: [IDBCursor](#idbcursor) -> Int -> ()
46− > `throw` TransactionInactiveError, TypeError, InvalidStateError
47−- continue :: [IDBCursor](#idbcursor) -> Maybe [IDBKey](#idbkey) -> ()
48− > `throw` TransactionInactivError, DataError, InvalidStateError
49−- continuePrimaryKey :: [IDBCursor](#idbcursor) -> [IDBKey](#idbkey) -> [IDBKey](#idbkey) -> ()
50− > `throw` TransactionInactiveError, DataError, InvalidStateError, InvalidAccessError
48+onUpgradeNeeded :: forall e. Database -> Transaction -> { oldVersion :: Int } -> Eff (idb :: IDB, exception :: EXCEPTION | e) Unit
49+onUpgradeNeeded db _ _ = launchAff_ do
50+ store <- IDBDatabase.createObjectStore db "store" IDBDatabase.defaultParameters
51+ _ <- IDBObjectStore.add store "patate" (Just 1)
52+ _ <- IDBObjectStore.add store { property: 42 } (Just 2)
53+ _ <- IDBObjectStore.createIndex store "index" ["property"] IDBObjectStore.defaultParameters
54+ pure unit
55+```
5156
57+## Notes
5258
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.
5463
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.
5570
56−#### IDBCursorWithValue
57−> https://developer.mozilla.org/en-US/docs/Web/API/IDBCursorwithValue
71+## Changelog
5872
59−Inherit from [IDBCursor](#idbcursor)
73+#### v3.0.0
6074
61−- delete :: [IDBCursor](#idbcursor) -> [IDBRequest](#idbrequest) ()
62− > `throw` TransactionInactiveError, ReadOnlyError, InvalidStateError
63−- update :: [IDBCursor](#idbcursor) -> a -> [IDBRequest](#idbrequest) a
64− > `throw` TransactionInactiveError, ReadOnlyError, InvalidStateError, DataError, DataCloneError
75+- callback to `onUpgradeNeeded` event now provide a record with the database old version.
6576
77+#### v2.0.0
6678
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.
6881
82+- make the Key more opaque (by having an IDBKey instance for Foreign types)
6983
70−#### IDBCursorDirection
71−> https://www.w3.org/TR/IndexedDB/#idl-def-IDBCursorDirection
84+- Upgrade purescript-exceptions to 3.1.0 and leverage the new `name` accessor
7285
73−data [IDBCursorDirection](#idbcursordirection)
74− = Next
75− | NextUnique
76− | Prev
77− | PrevUnique
7886
87+#### v1.0.0
7988
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
8192
93+## Documentation
8294
83−#### IDBDatabase
84−> https://www.w3.org/TR/IndexedDB/#idl-def-IDBDatabase
95+Module documentation is [published on Pursuit](http://pursuit.purescript.org/packages/purescript-indexeddb).
8596
86−- name :: String
87−- version :: Long
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
97+## Testing
98+Tested in the cloud on multiple browsers and operating systems thanks to [BrowserStack](https://www.browserstack.com)
9699
97−##### Events
98−- onabort
99−- onerror
100−- onversionchange
101100
101+| IE / Edge | Chrome | Firefox | Safari | Opera | Android | iOS Safari |
102+| ----------| ------ | ------- | ------- | ----- | ------- | ---------- |
103+| - | >= 57 | >= 51 | - | >= 46 | - | - |
102104
103−---
104−
105−
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
105+<p align="center">
106+ <a href="https://www.browserstack.com"><img alt="browserstack" src=".github/browserstack.png" /></a>
107+</p>
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-spec": "1.0.0",
13− "purescript-datetime": "3.2.0"
14− },
15− "devDependencies": {
16− "purescript-psci-support": "3.0.0",
17− "purescript-now": "^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 @@
11{
2− "name": "purescript-indexeddb",
2+ "name": "purescript-indexed-DB",
33 "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",
68 "scripts": {
7− "test:build": "pulp browserify -j 4 --main \"Test.Main\" -I test --to dist/karma/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"
9+ "test": "spago build && node index.js"
1410 },
1511 "dependencies": {
16− "eslint": "^3.19.0",
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"
12+ "fake-indexeddb": "^4.0.1"
2213 },
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",
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"
14+ "type": "module"
4615}
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) {
33};
44
55
6−exports._showCursor = function _showCursor(cursor) {
6+export function _showCursor(cursor) {
77 return '(IDBCursor ' +
88 '{ direction: ' + cursor.direction +
99 ', key: ' + cursor.key +
@@ -11,7 +11,7 @@ exports._showCursor = function _showCursor(cursor) {
1111 ' })';
1212};
1313
14−exports._showDatabase = function _showDatabase(db) {
14+export function _showDatabase(db) {
1515 return '(IDBDatabase ' +
1616 '{ name: ' + db.name +
1717 ', objectStoreNames: [' + toArray(db.objectStoreNames).join(', ') + ']' +
@@ -19,7 +19,7 @@ exports._showDatabase = function _showDatabase(db) {
1919 ' })';
2020};
2121
22−exports._showIndex = function _showIndex(index) {
22+export function _showIndex(index) {
2323 return '(IDBIndex ' +
2424 '{ name: ' + index.name +
2525 ', keyPath: ' + index.keyPath +
@@ -28,7 +28,7 @@ exports._showIndex = function _showIndex(index) {
2828 ' })';
2929};
3030
31−exports._showKeyRange = function _showKeyRange(range) {
31+export function _showKeyRange(range) {
3232 return '(IDBKeyRange ' +
3333 '{ lower: ' + range.lower +
3434 ', upper: ' + range.upper +
@@ -37,7 +37,7 @@ exports._showKeyRange = function _showKeyRange(range) {
3737 ' })';
3838};
3939
40−exports._showObjectStore = function _showObjectStore(store) {
40+export function _showObjectStore(store) {
4141 return '(IDBObjectStore ' +
4242 '{ autoIncrement: ' + store.autoIncrement +
4343 ', indexNames: [' + toArray(store.indexNames).join(', ') + ']' +
@@ -46,7 +46,7 @@ exports._showObjectStore = function _showObjectStore(store) {
4646 ' })';
4747};
4848
49−exports._showTransaction = function _showTransaction(tx) {
49+export function _showTransaction(tx) {
5050 return '(IDBTransaction ' +
5151 '{ error: ' + tx.error +
5252 ', mode: ' + tx.mode +
src/Database/IndexedDB/Core.purs+165−56
@@ -1,133 +1,242 @@
1+-- | The Core module gathers types used across the library and provides basic Show instances for
2+-- | those types.
13module Database.IndexedDB.Core
2− ( class FromString, parse
3− , IDB
4− , CursorDirection(..)
5− , CursorSource(..)
6− , Database
4+ -- * Effects
5+ (
6+ -- IDB
7+
8+ -- * Types
9+ Database
710 , Index
811 , KeyCursor
912 , KeyRange
10− , KeyPath
1113 , ObjectStore
1214 , Transaction
13− , TransactionMode(..)
1415 , 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
1531 , module Database.IndexedDB.IDBKey
1632 ) where
1733
18−import Prelude (class Show)
34+import Prelude (class Show)
1935
20−import Control.Monad.Eff (kind Effect)
21−import Data.Maybe (Maybe(..))
36+import Data.Maybe (Maybe(..))
37+import Data.String.Read (class Read)
2238
2339import Database.IndexedDB.IDBKey
2440
41+--------------------
42+-- TYPES
43+--
2544
26−foreign import data IDB :: Effect
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.
47+foreign import data Database :: Type
2748
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
2852
29−data CursorDirection = Next | NextUnique | Prev | PrevUnique
53+-- | A key range is a continuous interval over some data type used for keys.
54+foreign import data KeyRange :: Type
3055
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
3159
32−data CursorSource = ObjectStore ObjectStore | Index Index
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
3363
64+-- | An object store is the primary storage mechanism for storing data in a database.
65+foreign import data ObjectStore :: Type
3466
35−data TransactionMode = ReadOnly | ReadWrite | VersionChange
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
3670
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
3781
38−type KeyPath = Array String
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
3990
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
4099
41−foreign import data Database :: Type
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.
42107
108+--------------------
109+-- Classes
110+--
43111
44−foreign import data Index :: 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
45115
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
46120
47−foreign import data KeyCursor :: Type
121+-- | The IDBDatabase interface represents a connection to a database.
122+class IDBDatabase db
48123
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
49128
50−foreign import data KeyRange :: Type
129+-- | The IDBKeyRange interface represents a key range.
130+class IDBKeyRange range
51131
132+-- | The IDBtransaction interface.
133+class IDBTransaction tx
52134
53−foreign import data ObjectStore :: Type
135+-- | The IDBObjectStore interface represents an object store handle.
136+class IDBObjectStore store
54137
138+--------------------
139+-- INSTANCES
140+--
55141
56−foreign import data Transaction :: Type
142+instance idbCursorKeyCursor :: IDBCursor KeyCursor
57143
144+instance idbCursorValueCursor :: IDBCursor ValueCursor
58145
59−foreign import data ValueCursor :: Type
146+instance idbConcreteCursorKeyCursor :: IDBConcreteCursor KeyCursor
60147
148+instance idbConcreteCursorValueCursor :: IDBConcreteCursor ValueCursor
61149
62−class FromString a where
63− parse :: String -> Maybe a
150+instance idbDatabaseDatabase :: IDBDatabase Database
64151
152+instance idbIndexIndex :: IDBIndex Index
65153
66−foreign import _showCursor :: forall cursor. cursor -> String
67−instance showKeyCursor :: Show KeyCursor where
68− show = _showCursor
154+instance idbIndexObjectStore :: IDBIndex ObjectStore
69155
156+instance idbKeyRangeKeyRange :: IDBKeyRange KeyRange
70157
71−instance showValueCursor :: Show ValueCursor where
72− show = _showCursor
158+instance idbObjectStoreObjectStore :: IDBObjectStore ObjectStore
73159
160+instance idbTransactionTransaction :: IDBTransaction Transaction
74161
75−foreign import _showDatabase :: Database -> String
76−instance showDatabase :: Show Database where
77− show = _showDatabase
162+instance showKeyCursor :: Show KeyCursor where
163+ show = _showCursor
78164
165+instance showValueCursor :: Show ValueCursor where
166+ show = _showCursor
79167
80−foreign import _showIndex :: Index -> String
81168instance showIndex :: Show Index where
82169 show = _showIndex
83170
84−
85−foreign import _showKeyRange :: KeyRange -> String
86171instance showKeyRange :: Show KeyRange where
87172 show = _showKeyRange
88173
89−
90−foreign import _showObjectStore :: ObjectStore -> String
91174instance showObjectStore :: Show ObjectStore where
92175 show = _showObjectStore
93176
94−
95−foreign import _showTransaction :: Transaction -> String
96177instance showTransaction :: Show Transaction where
97178 show = _showTransaction
98179
180+instance showDatabase :: Show Database where
181+ show = _showDatabase
99182
100183instance showCursorDirection :: Show CursorDirection where
101184 show x =
102185 case x of
103− Next -> "next"
186+ Next -> "next"
104187 NextUnique -> "nextunique"
105− Prev -> "prev"
188+ Prev -> "prev"
106189 PrevUnique -> "prevunique"
107190
108−
109191instance showTransactionMode :: Show TransactionMode where
110192 show x =
111193 case x of
112− ReadOnly -> "readonly"
113− ReadWrite -> "readwrite"
194+ ReadOnly -> "readonly"
195+ ReadWrite -> "readwrite"
114196 VersionChange -> "versionchange"
115197
116−
117−instance fromStringCursorDirection :: FromString CursorDirection where
118− parse s =
198+instance readCursorDirection :: Read CursorDirection where
199+ read s =
119200 case s of
120− "next" -> Just Next
201+ "next" -> Just Next
121202 "nextunique" -> Just NextUnique
122− "prev" -> Just Prev
203+ "prev" -> Just Prev
123204 "prevunique" -> Just PrevUnique
124− _ -> Nothing
125−
205+ _ -> Nothing
126206
127−instance fromStringTransactionMode :: FromString TransactionMode where
128− parse s =
207+instance readTransactionMode :: Read TransactionMode where
208+ read s =
129209 case s of
130− "readonly" -> Just ReadOnly
131− "readwrite" -> Just ReadWrite
210+ "readonly" -> Just ReadOnly
211+ "readwrite" -> Just ReadWrite
132212 "versionchange" -> Just VersionChange
133− _ -> 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+75−25
@@ -1,6 +1,6 @@
11const errorHandler = function errorHandler(cb) {
22 return function _handler(e) {
3− cb(new Error(e.target.error.name));
3+ cb(e.target.error);
44 };
55};
66
@@ -10,85 +10,135 @@ const successHandler = function successHandler(cb) {
1010 };
1111};
1212
13−
14−exports._advance = function _advance(cursor, count) {
15− return function aff(success, error) {
13+export function _advance(cursor, count) {
14+ return function aff(error, success) {
1615 try {
1716 cursor.advance(count);
1817 success();
1918 } catch (e) {
20− error(new Error(e.name));
19+ error(e);
2120 }
21+
22+ return function canceler(_, cancelerError) {
23+ cancelerError(new Error("Can't cancel IDB Effects"));
24+ };
2225 };
2326};
2427
25−exports._continue = function _continue(cursor, key) {
26− return function aff(success, error) {
28+export function _continue(cursor, key) {
29+ return function aff(error, success) {
2730 try {
2831 cursor.continue(key || undefined);
32+ success();
2933 } catch (e) {
30− error(new Error(e.name));
34+ error(e);
3135 }
36+
37+ return function canceler(_, cancelerError) {
38+ cancelerError(new Error("Can't cancel IDB Effects"));
39+ };
3240 };
3341};
3442
35−exports._continuePrimaryKey = function _continuePrimaryKey(cursor, key, primaryKey) {
36− return function aff(success, error) {
43+export function _continuePrimaryKey(cursor, key, primaryKey) {
44+ return function aff(error, success) {
3745 try {
3846 cursor.continuePrimaryKey(key, primaryKey);
47+ success();
3948 } catch (e) {
40− error(new Error(e.name));
49+ error(e);
4150 }
51+
52+ return function canceler(_, cancelerError) {
53+ cancelerError(new Error("Can't cancel IDB Effects"));
54+ };
4255 };
4356};
4457
45−exports._delete = function _delete(cursor) {
46− return function aff(success, error) {
58+export function _delete(cursor) {
59+ return function aff(error, success) {
4760 try {
4861 const request = cursor.delete();
4962 request.onsuccess = successHandler(success);
5063 request.onerror = errorHandler(error);
5164 } catch (e) {
52− error(new Error(e.name));
65+ error(e);
5366 }
67+
68+ return function canceler(_, cancelerError) {
69+ cancelerError(new Error("Can't cancel IDB Effects"));
70+ };
5471 };
5572};
5673
57−exports._direction = function _direction(fromString, cursor) {
74+export function _direction(fromString, cursor) {
5875 return fromString(cursor.direction);
5976};
6077
61−exports._key = function _key(cursor) {
62− return cursor.key;
78+export function _key(cursor) {
79+ return function aff(error, success) {
80+ try {
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+ };
6390};
6491
65−exports._primaryKey = function _primaryKey(cursor) {
66− return cursor.primaryKey;
92+export function _primaryKey(cursor) {
93+ return function aff(error, success) {
94+ try {
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+ };
67104};
68105
69−exports._source = function _source(IDBObjectStore, IDBIndex, cursor) {
106+export function _source(IDBObjectStore, IDBIndex, cursor) {
70107 switch (cursor.source.constructor.name) {
71108 case 'IDBIndex':
72109 return IDBIndex(cursor.source);
73110 case 'IDBObjectStore':
74111 return IDBObjectStore(cursor.source);
75112 default:
76− throw new Error('UnexpectedCursorSource');
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+ });
77123 }
78124};
79125
80−exports._update = function _update(cursor, value) {
81− return function aff(success, error) {
126+export function _update(cursor, value) {
127+ return function aff(error, success) {
82128 try {
83129 const request = cursor.update(value);
84130 request.onsuccess = successHandler(success);
85131 request.onerror = errorHandler(error);
86132 } catch (e) {
87− error(new Error(e.name));
133+ error(e);
88134 }
135+
136+ return function canceler(_, cancelerError) {
137+ cancelerError(new Error("Can't cancel IDB Effects"));
138+ };
89139 };
90140};
91141
92−exports._value = function _value(cursor) {
142+export function _value(cursor) {
93143 return cursor.value;
94144};
src/Database/IndexedDB/IDBCursor.purs+154−106
@@ -1,138 +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.
13module Database.IndexedDB.IDBCursor
2− ( class IDBCursor, advance, continue, continuePrimaryKey, delete, update
4+ -- * Interface
5+ ( advance
6+ , continue
7+ , continuePrimaryKey
8+ , delete
9+ , update
10+
11+ -- * Attributes
312 , direction
413 , key
514 , primaryKey
615 , source
7− , direction'
8− , key'
9− , primaryKey'
10− , source'
1116 , value
1217 ) where
1318
14−import Prelude (Unit, (>>>))
15−
16−import Control.Monad.Aff (Aff)
17−import Data.Function.Uncurried as Fn
18−import Data.Function.Uncurried (Fn2, Fn3)
19−import Data.Maybe (Maybe)
20−import Data.Nullable (Nullable, toNullable)
21−import Data.Foreign (Foreign, toForeign, unsafeFromForeign)
22−
2319import Database.IndexedDB.Core
2420
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)
2531
2632--------------------
2733-- INTERFACES
2834--
29−class IDBCursor cursor where
30− advance :: forall e. cursor -> Int -> Aff (idb :: IDB | e) Unit
31− continue :: forall e. cursor -> Maybe Key -> Aff (idb :: IDB | e) Unit
32− continuePrimaryKey :: forall e. cursor -> Key -> Key -> Aff (idb :: IDB | e) Unit
33− delete :: forall e. cursor -> Aff (idb ::IDB | e) Unit
34− update :: forall val e. cursor -> val -> Aff (idb :: IDB | e) Key
3535
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
3690
3791--------------------
3892-- ATTRIBUTES
3993--
40−direction :: KeyCursor -> CursorDirection
41−direction =
42− Fn.runFn2 _direction (parse >>> toNullable)
4394
44−
45−key :: KeyCursor -> Key
95+-- | Returns the direction (Next|NextUnique|Prev|PrevUnique) of the cursor.
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
46111key =
47− _key
48−
49−
50−primaryKey :: KeyCursor -> Key
112+ map toKey <<< fromEffectFnAff <<< _key
113+
114+-- | Returns the effective key of the cursor. Throws a "InvalidStateError" DOMException
115+-- | if the cursor is advancing or is finished.
116+primaryKey
117+ :: forall cursor
118+ . (IDBConcreteCursor cursor)
119+ => cursor
120+ -> Aff Key
51121primaryKey =
52− _primaryKey
53−
54−
55−source :: KeyCursor -> CursorSource
122+ map toKey <<< fromEffectFnAff <<< _primaryKey
123+
124+-- | Returns the IDBObjectStore or IDBIndex the cursor was opened from.
125+source
126+ :: forall cursor
127+ . (IDBConcreteCursor cursor)
128+ => cursor
129+ -> CursorSource
56130source =
57131 Fn.runFn3 _source ObjectStore Index
58132
59−
60−-- NOTE: Polymorphism not used here cause *we want* those methods to be distincs for
61−-- the concrete types mostly for consistency with the Database.IndexedDB.IDBIndex module
62−-- where the IDBIndex interfaces is shared by Index and ObjectStore, but attributes of
63−-- respective concrete types aren't shared.
64−direction' :: ValueCursor -> CursorDirection
65−direction' =
66− Fn.runFn2 _direction (parse >>> toNullable)
67−
68−
69−key' :: ValueCursor -> Key
70−key' =
71− _key
72−
73−
74−primaryKey' :: ValueCursor -> Key
75−primaryKey' =
76− _primaryKey
77−
78−
79−source' :: ValueCursor -> CursorSource
80−source' =
81− Fn.runFn3 _source ObjectStore Index
82−
83−
84−value :: forall val. ValueCursor -> val
133+value
134+ :: forall val
135+ . ValueCursor
136+ -> val
85137value =
86138 _value >>> unsafeFromForeign
87139
88−
89−--------------------
90−-- INSTANCES
91−--
92−instance keyCursorKeyCursor :: IDBCursor KeyCursor where
93− advance = Fn.runFn2 _advance
94− continue c mk = Fn.runFn2 _continue c (toNullable mk)
95− continuePrimaryKey = Fn.runFn3 _continuePrimaryKey
96− delete = _delete
97− update c = toForeign >>> Fn.runFn2 _update c
98−
99−
100−instance valueCursorKeyCursor :: IDBCursor ValueCursor where
101− advance = Fn.runFn2 _advance
102− continue c mk = Fn.runFn2 _continue c (toNullable mk)
103− continuePrimaryKey = Fn.runFn3 _continuePrimaryKey
104− delete = _delete
105− update c = toForeign >>> Fn.runFn2 _update c
106−
107−
108140--------------------
109141-- FFI
110142--
111−foreign import _advance :: forall cursor e. Fn2 cursor Int (Aff (idb :: IDB | e) Unit)
112−
113−
114−foreign import _continue :: forall cursor e. Fn2 cursor (Nullable Key) (Aff (idb :: IDB | e) Unit)
115−
116−
117−foreign import _continuePrimaryKey :: forall cursor e. Fn3 cursor Key Key (Aff (idb :: IDB | e) Unit)
118−
119−
120−foreign import _delete :: forall cursor e. cursor -> (Aff (idb ::IDB | e) Unit)
121−
122−
123−foreign import _direction :: forall cursor. Fn2 (String -> Nullable CursorDirection) cursor CursorDirection
124−
125−
126−foreign import _key :: forall cursor. cursor -> Key
127−
128−
129−foreign import _primaryKey :: forall cursor. cursor -> Key
130−
131−
132−foreign import _source :: forall cursor. Fn3 (ObjectStore -> CursorSource) (Index -> CursorSource) cursor CursorSource
133−
134−
135−foreign import _update :: forall cursor e. Fn2 cursor Foreign (Aff (idb :: IDB | e) Key)
136−
137143
138−foreign import _value :: forall cursor val. cursor -> val
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
184+ :: forall cursor val
185+ . cursor
186+ -> val
src/Database/IndexedDB/IDBDatabase.js+62−28
@@ -1,21 +1,25 @@
1−const $Core = require('Database.IndexedDB.Core/foreign');
2−
3−const toArray = $Core.toArray;
1+const toArray = function toArray(xs) {
2+ return Array.prototype.slice.apply(xs);
3+};
44
55
6−exports._close = function _close(db) {
7− return function aff(success, error) {
6+export function _close(db) {
7+ return function aff(error, success) {
88 try {
99 db.close();
1010 success();
1111 } catch (e) {
12− error(new Error(e.name));
12+ error(e);
1313 }
14+
15+ return function canceler(_, cancelerError) {
16+ cancelerError(new Error("Can't cancel IDB Effects"));
17+ };
1418 };
1519};
1620
17−exports._createObjectStore = function _createObjectStore(db, name, opts) {
18− return function aff(success, error) {
21+export function _createObjectStore(db, name, opts) {
22+ return function aff(error, success) {
1923 var keyPath;
2024
2125 try {
@@ -39,77 +43,107 @@ exports._createObjectStore = function _createObjectStore(db, name, opts) {
3943 });
4044 success(store);
4145 } catch (e) {
42− error(new Error(e.name));
46+ error(e);
4347 }
48+
49+ return function canceler(_, cancelerError) {
50+ cancelerError(new Error("Can't cancel IDB Effects"));
51+ };
4452 };
4553};
4654
47−exports._deleteObjectStore = function _deleteObjectStore(db, name) {
48− return function aff(success, error) {
55+export function _deleteObjectStore(db, name) {
56+ return function aff(error, success) {
4957 try {
5058 db.deleteObjectStore(name);
5159 success();
5260 } catch (e) {
53− error(new Error(e.name));
61+ error(e);
5462 }
63+
64+ return function canceler(_, cancelerError) {
65+ cancelerError(new Error("Can't cancel IDB Effects"));
66+ };
5567 };
5668};
5769
58−exports._name = function _name(db) {
70+export function _name(db) {
5971 return db.name;
6072};
6173
62−exports._objectStoreNames = function _objectStoreNames(db) {
74+export function _objectStoreNames(db) {
6375 return toArray(db.objectStoreNames);
6476};
6577
66−exports._onAbort = function _onAbort(db, f) {
67− return function aff(success) {
78+export function _onAbort(db, f) {
79+ return function aff(error, success) {
6880 db.onabort = function onabort() {
6981 f();
7082 };
7183 success();
84+
85+ return function canceler(_, cancelerError) {
86+ cancelerError(new Error("Can't cancel IDB Effects"));
87+ };
7288 };
7389};
7490
75−exports._onClose = function _onClose(db, f) {
76− return function aff(success) {
91+export function _onClose(db, f) {
92+ return function aff(error, success) {
7793 db.onclose = function onclose() {
7894 f();
7995 };
8096 success();
97+
98+ return function canceler(_, cancelerError) {
99+ cancelerError(new Error("Can't cancel IDB Effects"));
100+ };
81101 };
82102};
83103
84−exports._onError = function _onError(db, f) {
85− return function aff(success) {
104+export function _onError(db, f) {
105+ return function aff(error, success) {
86106 db.onerror = function onerror(e) {
87− f(new Error(e.target.error.name))();
107+ f(e.target.error)();
88108 };
89109 success();
110+
111+ return function canceler(_, cancelerError) {
112+ cancelerError(new Error("Can't cancel IDB Effects"));
113+ };
90114 };
91115};
92116
93−exports._onVersionChange = function _onVersionChange(db, f) {
94− return function aff(success) {
117+export function _onVersionChange(db, f) {
118+ return function aff(error, success) {
95119 db.onversionchange = function onversionchange(e) {
96120 f({ oldVersion: e.oldVersion, newVersion: e.newVersion })();
97121 };
98122 success();
123+
124+ return function canceler(_, cancelerError) {
125+ cancelerError(new Error("Can't cancel IDB Effects"));
126+ };
99127 };
100128};
101129
102−exports._transaction = function _transaction(db, stores, mode) {
103− return function aff(success, error) {
130+export function _transaction(db, stores, mode) {
131+ return function aff(error, success) {
132+ var transaction;
104133 try {
105− const transaction = db.transaction(stores, mode);
134+ transaction = db.transaction(stores, mode);
106135 success(transaction);
107136 } catch (e) {
108− error(new Error(e.name));
137+ error(e);
109138 }
139+
140+ return function canceler(_, cancelerError, cancelerSuccess) {
141+ transaction.abort();
142+ cancelerSuccess();
143+ };
110144 };
111145};
112146
113−exports._version = function _version(db) {
147+export function _version(db) {
114148 return db.version;
115149};
src/Database/IndexedDB/IDBDatabase.purs+160−75
@@ -1,130 +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.
13module Database.IndexedDB.IDBDatabase
2− ( class IDBDatabase, close, createObjectStore, deleteObjectStore, transaction
3− , StoreName
4+ -- * Types
5+ ( StoreName
6+ , ObjectStoreParameters
7+ , defaultParameters
8+
9+ -- * Interface
10+ , close
11+ , createObjectStore
12+ , deleteObjectStore
13+ , transaction
14+
15+ -- * Attributes
416 , name
517 , objectStoreNames
618 , version
19+
20+ -- * Event handlers
721 , onAbort
822 , onClose
923 , onError
1024 , onVersionChange
1125 ) where
1226
13−import Prelude (Unit, show)
14−
15−import Control.Monad.Aff (Aff)
16−import Control.Monad.Eff (Eff)
17−import Control.Monad.Eff.Exception (Error)
18−import Data.Function.Uncurried as Fn
19−import Data.Function.Uncurried (Fn2, Fn3, Fn4)
20−
2127import Database.IndexedDB.Core
22−import Database.IndexedDB.IDBObjectStore (IDBObjectStoreParameters)
2328
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, (<<<), ($))
2436
2537--------------------
26−-- INTERFACE
38+-- TYPES
2739--
28−class IDBDatabase db where
29− close :: forall e. db -> Aff (idb :: IDB | e) Unit
30− createObjectStore :: forall e. db -> StoreName -> IDBObjectStoreParameters -> Aff (idb :: IDB | e) ObjectStore
31− deleteObjectStore :: forall e. db -> StoreName -> Aff (idb :: IDB | e) ObjectStore
32− transaction :: forall e. db -> Array StoreName -> TransactionMode -> Aff (idb :: IDB | e) Transaction
33−
3440
41+-- | Type alias for StoreName
3542type StoreName = String
3643
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')
37105
38106--------------------
39107-- ATTRIBUTES
40108--
41−name :: Database -> String
109+
110+-- | Returns the name of the database.
111+name
112+ :: Database
113+ -> String
42114name =
43115 _name
44116
45−
46−objectStoreNames :: Database -> Array String
117+-- | Returns a list of the names of object stores in the database.
118+objectStoreNames
119+ :: Database
120+ -> Array String
47121objectStoreNames =
48122 _objectStoreNames
49123
50−
51−version :: Database -> Int
124+-- | Returns the version of the database.
125+version
126+ :: Database
127+ -> Int
52128version =
53129 _version
54130
55−
56131--------------------
57132-- EVENT HANDLERS
58133--
59−onAbort :: forall e e'. Database -> Eff ( | e') Unit -> Aff (idb :: IDB | e) Unit
134+-- | Event handler for the `abort` event.
135+onAbort
136+ :: Database
137+ -> Effect Unit
138+ -> Aff Unit
60139onAbort db f =
61− Fn.runFn2 _onAbort db f
140+ fromEffectFnAff $ Fn.runFn2 _onAbort db f
62141
63−
64−onClose :: forall e e'. Database -> Eff ( | e') Unit -> Aff (idb :: IDB | e) Unit
142+-- | Event handler for the `close` event.
143+onClose
144+ :: Database
145+ -> Effect Unit
146+ -> Aff Unit
65147onClose db f =
66− Fn.runFn2 _onClose db f
67−
148+ fromEffectFnAff $ Fn.runFn2 _onClose db f
68149
69−onError :: forall e e'. Database -> (Error -> Eff ( | e') Unit) -> Aff (idb :: IDB | e) Unit
150+-- | Event handler for the `error` event.
151+onError
152+ :: Database
153+ -> (Error -> Effect Unit)
154+ -> Aff Unit
70155onError db f =
71− Fn.runFn2 _onError db f
72−
73−
74−onVersionChange :: forall e e'. Database -> ({ oldVersion :: Int, newVersion :: Int } -> Eff ( | e') Unit) -> Aff (idb :: IDB | e) Unit
156+ fromEffectFnAff $ Fn.runFn2 _onError db f
157+
158+-- | Event handler for the `versionchange` event.
159+onVersionChange
160+ :: Database
161+ -> ( { oldVersion :: Int, newVersion :: Int }
162+ -> Effect Unit
163+ )
164+ -> Aff Unit
75165onVersionChange db f =
76− Fn.runFn2 _onVersionChange db f
77−
78−
79−
80−--------------------
81−-- INSTANCES
82−--
83−instance idbDatabaseDatabase :: IDBDatabase Database where
84− close =
85− _close
86−
87− createObjectStore db name' opts =
88− Fn.runFn3 _createObjectStore db name' opts
89−
90− deleteObjectStore db name' =
91− Fn.runFn2 _deleteObjectStore db name'
92−
93− transaction db stores mode' =
94− Fn.runFn3 _transaction db stores (show mode')
95−
166+ fromEffectFnAff $ Fn.runFn2 _onVersionChange db f
96167
97168--------------------
98169-- FFI
99170--
100−foreign import _close :: forall db e. db -> Aff (idb :: IDB | e) Unit
101−
102−
103−foreign import _createObjectStore :: forall db e. Fn3 db String { keyPath :: Array String, autoIncrement :: Boolean } (Aff (idb :: IDB | e) ObjectStore)
104−
105−
106−foreign import _deleteObjectStore :: forall db e. Fn2 db String (Aff (idb :: IDB | e) ObjectStore)
107−
108−
109−foreign import _name :: Database -> String
110−
111−
112−foreign import _objectStoreNames :: Database -> Array String
113−
114171
115−foreign import _onAbort :: forall db e e'. Fn2 db (Eff ( | e') Unit) (Aff (idb :: IDB | e) Unit)
172+foreign import _close
173+ :: forall db
174+ . db
175+ -> EffectFnAff Unit
116176
177+foreign import _createObjectStore
178+ :: forall db
179+ . Fn3 db String { keyPath :: Array String, autoIncrement :: Boolean } (EffectFnAff ObjectStore)
117180
118−foreign import _onClose :: forall db e e'. Fn2 db (Eff ( | e') Unit) (Aff (idb :: IDB | e) Unit)
181+foreign import _deleteObjectStore
182+ :: forall db
183+ . Fn2 db String (EffectFnAff ObjectStore)
119184
185+foreign import _name
186+ :: Database
187+ -> String
120188
121−foreign import _onError :: forall db e e'. Fn2 db (Error -> Eff ( | e') Unit) (Aff (idb :: IDB | e) Unit)
189+foreign import _objectStoreNames
190+ :: Database
191+ -> Array String
122192
193+foreign import _onAbort
194+ :: forall db
195+ . Fn2 db (Effect Unit) (EffectFnAff Unit)
123196
124−foreign import _onVersionChange :: forall db e e'. Fn2 db ({ oldVersion :: Int, newVersion :: Int } -> Eff ( | e') Unit) (Aff (idb :: IDB | e) Unit)
197+foreign import _onClose
198+ :: forall db
199+ . Fn2 db (Effect Unit) (EffectFnAff Unit)
125200
201+foreign import _onError
202+ :: forall db
203+ . Fn2 db (Error -> Effect Unit) (EffectFnAff Unit)
126204
127−foreign import _transaction :: forall db e. Fn3 db (Array String) String (Aff (idb :: IDB | e) Transaction)
205+foreign import _onVersionChange
206+ :: forall db
207+ . Fn2 db ({ oldVersion :: Int, newVersion :: Int } -> Effect Unit) (EffectFnAff Unit)
128208
209+foreign import _transaction
210+ :: forall db
211+ . Fn3 db (Array String) String (EffectFnAff Transaction)
129212
130−foreign import _version :: Database -> Int
213+foreign import _version
214+ :: Database
215+ -> Int
src/Database/IndexedDB/IDBFactory.js+21−8
@@ -1,6 +1,6 @@
11const errorHandler = function errorHandler(cb) {
22 return function _handler(e) {
3− cb(new Error(e.target.error.name));
3+ cb(e.target.error);
44 };
55};
66
@@ -14,9 +14,12 @@ const noOp2 = function noOp2() {
1414 return noOp;
1515};
1616
17+const noOp3 = function noOp3() {
18+ return noOp2;
19+};
1720
18−exports._deleteDatabase = function _deleteDatabase(name) {
19− return function aff(success, error) {
21+export function _deleteDatabase(name) {
22+ return function aff(error, success) {
2023 try {
2124 const request = indexedDB.deleteDatabase(name);
2225
@@ -26,15 +29,19 @@ exports._deleteDatabase = function _deleteDatabase(name) {
2629
2730 request.onerror = errorHandler(error);
2831 } catch (e) {
29− error(new Error(e.name));
32+ error(e);
3033 }
34+
35+ return function canceler(_, cancelerError) {
36+ cancelerError(new Error("Can't cancel IDB Effects"));
37+ };
3138 };
3239};
3340
34−exports._open = function _open(fromMaybe, name, mver, req) {
41+export function _open(fromMaybe, name, mver, req) {
3542 const ver = fromMaybe(undefined)(mver);
3643
37− return function aff(success, error) {
44+ return function aff(error, success) {
3845 try {
3946 const request = indexedDB.open(name, ver);
4047 request.onsuccess = function onSuccess(e) {
@@ -46,12 +53,18 @@ exports._open = function _open(fromMaybe, name, mver, req) {
4653 };
4754
4855 request.onupgradeneeded = function onUpgradeNeeded(e) {
49− fromMaybe(noOp2)(req.onUpgradeNeeded)(e.target.result)(e.target.transaction)();
56+ const meta = { oldVersion: e.oldVersion };
57+ // eslint-disable-next-line max-len
58+ fromMaybe(noOp3)(req.onUpgradeNeeded)(e.target.result)(e.target.transaction)(meta)();
5059 };
5160
5261 request.onerror = errorHandler(error);
5362 } catch (e) {
54− error(new Error(e.name));
63+ error(e);
5564 }
65+
66+ return function canceler(_, cancelerError) {
67+ cancelerError(new Error("Can't cancel IDB Effects"));
68+ };
5669 };
5770};
src/Database/IndexedDB/IDBFactory.purs+61−21
@@ -1,39 +1,79 @@
1−module Database.IndexedDB.IDBFactory where
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.
4+module Database.IndexedDB.IDBFactory
5+ -- * Types
6+ ( Callbacks
7+ , DatabaseName
8+ , Version
29
3−import Prelude (Unit)
4−
5−import Control.Monad.Aff (Aff)
6−import Control.Monad.Eff (Eff)
7−import Data.Function.Uncurried as Fn
8−import Data.Function.Uncurried (Fn4)
9−import Data.Maybe (Maybe, fromMaybe)
10+ -- * Interface
11+ , deleteDatabase
12+ , open
13+ ) where
1014
1115import Database.IndexedDB.Core
1216
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, ($), (<<<))
1324
1425--------------------
15−-- INTERFACE
26+-- TYPES
1627--
17−type OpenRequest e =
18− { onBlocked :: Maybe (Eff (| e) Unit)
19− , onUpgradeNeeded :: Maybe (Database -> Transaction -> Eff (| e) Unit)
28+
29+-- Type alias for binding listeners to an initial open action.
30+type Callbacks =
31+ { onBlocked :: Maybe (Effect Unit)
32+ , onUpgradeNeeded :: Maybe (Database -> Transaction -> { oldVersion :: Int } -> Effect Unit)
2033 }
2134
35+-- | Type alias for DatabaseName.
36+type DatabaseName = String
2237
23−deleteDatabase :: forall e. String -> Aff (idb :: IDB | e) Int
24−deleteDatabase =
25− _deleteDatabase
38+-- | Type alias for Version.
39+type Version = Int
2640
41+--------------------
42+-- INTERFACE
43+--
2744
28−open :: forall e e'. String -> Maybe Int -> OpenRequest e' -> Aff (idb :: IDB | e) Database
29−open name mver req =
30− Fn.runFn4 _open fromMaybe name mver req
45+-- | Attempts to delete the named database. If the database already exists
46+-- | and there are open connections that don’t close in response to a
47+-- | `versionchange` event, the request will be blocked until all they close.
48+deleteDatabase
49+ :: DatabaseName
50+ -> Aff Int
51+deleteDatabase =
52+ fromEffectFnAff <<< _deleteDatabase
3153
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
3269
3370--------------------
3471-- FFI
3572--
36−foreign import _deleteDatabase :: forall e. String -> Aff (idb :: IDB | e) Int
37−
73+foreign import _deleteDatabase
74+ :: String
75+ -> EffectFnAff Int
3876
39−foreign import _open :: forall a e e'. Fn4 (a -> Maybe a -> a) String (Maybe Int) (OpenRequest e') (Aff (idb :: IDB | e) Database)
77+foreign import _open
78+ :: forall a
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.
13module Database.IndexedDB.IDBIndex
2− ( module Database.IndexedDB.IDBIndex.Internal
3− ) where
4+ -- * Types
5+ ( Callbacks
6+
7+ -- * Interface
8+ , count
9+ , get
10+ , getAllKeys
11+ , getKey
12+ , openCursor
13+ , openKeyCursor
414
5−import Database.IndexedDB.IDBIndex.Internal
6− ( class IDBIndex, count, get, getAllKeys, getKey, openCursor, openKeyCursor
7− , IDBIndexParameters
15+ -- * Attributes
816 , keyPath
917 , multiEntry
1018 , name
1119 , objectStore
1220 , unique
13− , defaultParameters
14− )
21+ ) where
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−129
@@ -1,129 +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− try {
59− const request = index.get(range);
60− request.onsuccess = successHandler(success);
61− request.onerror = errorHandler(error);
62− } catch (e) {
63− error(new Error(e.name));
64− }
65− };
66−};
67−
68−/*
69− * NOTE: Require some additional work. The array (which isn't necessarily a list of
70− * polymorphic types in js) can't be easily translated to a PureScript array.
71− *
72− * However, it may be doable to convert the result to some key / value structure with values of
73− * different types.
74−exports._getAll = function _getAll(index, query, count) {
75− return function aff(success, error) {
76− const request = index.getAll(query, count);
77− request.onsuccess = successHandler(success);
78− request.onerror = errorHandler(error);
79− };
80−};
81−*/
82−
83−exports._getAllKeys = function _getAllKeys(index, range, count) {
84− return function aff(success, error) {
85− try {
86− const request = index.getAllKeys(range, count || undefined);
87− request.onsuccess = successHandler(success);
88− request.onerror = errorHandler(error);
89− } catch (e) {
90− error(new Error(e.name));
91− }
92− };
93−};
94−
95−exports._getKey = function _getKey(index, range) {
96− return function aff(success, error) {
97− try {
98− const request = index.getKey(range);
99− request.onsuccess = successHandler(success);
100− request.onerror = errorHandler(error);
101− } catch (e) {
102− error(new Error(e.name));
103− }
104− };
105−};
106−
107−exports._openCursor = function _openCursor(index, query, dir) {
108− return function aff(success, error) {
109− try {
110− const request = index.openCursor(query, dir);
111− request.onsuccess = successHandler(success);
112− request.onerror = errorHandler(error);
113− } catch (e) {
114− error(new Error(e.name));
115− }
116− };
117−};
118−
119−exports._openKeyCursor = function _openKeyCursor(index, query, dir) {
120− return function aff(success, error) {
121− try {
122− const request = index.openKeyCursor(query, dir);
123− request.onsuccess = successHandler(success);
124− request.onerror = errorHandler(error);
125− } catch (e) {
126− error(new Error(e.name));
127− }
128− };
129−};
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.
14module Database.IndexedDB.IDBKey
25 ( module Database.IndexedDB.IDBKey.Internal
36 ) where
47
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) {
22 return new Date(y, m, d, h, mi, s, ms);
33};
44
5−exports._readDateTime = function _readDateTime(parse, right, left, date) {
5+export function _readDateTime(parse, right, left, date) {
66 if (Object.getPrototypeOf(date) !== Date.prototype) {
77 return left(typeof date);
88 }
@@ -24,7 +24,7 @@ exports._readDateTime = function _readDateTime(parse, right, left, date) {
2424 return right(mdate);
2525};
2626
27−exports._unsafeReadDateTime = function _unsafeReadDateTime(parse, date) {
27+export function _unsafeReadDateTime(parse, date) {
2828 const y = date.getFullYear();
2929 const m = date.getMonth() + 1;
3030 const d = date.getDate();
src/Database/IndexedDB/IDBKey/Internal.purs+114−99
@@ -1,125 +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.
14module Database.IndexedDB.IDBKey.Internal
2− ( Key(..)
3− , class IDBKey, toKey , fromKey , unsafeFromKey
4− , extractForeign
5− ) where
5+ ( Key
6+ , class IDBKey
7+ , toKey
8+ , fromKey
9+ , unsafeFromKey
10+ , none
11+ ) where
612
713import Prelude
814
9−import Control.Alt ((<|>))
10−import Control.Monad.Except (ExceptT(..), runExceptT)
11−import Data.Date as Date
12−import Data.DateTime (DateTime(..), Time(..))
13−import Data.Enum (fromEnum, toEnum)
14−import Data.Foreign as Foreign
15−import Data.Foreign (Foreign, F)
16−import Data.Function.Uncurried as Fn
17−import Data.Function.Uncurried (Fn2, Fn4, Fn7)
18−import Data.List.NonEmpty (NonEmptyList(..))
19−import Data.List.Types (List(..))
20−import Data.NonEmpty (NonEmpty(..))
21−import Data.Either (Either(..), either, isRight)
22−import Data.Identity (Identity(..))
23−import Data.Nullable (Nullable, toNullable)
24−import Data.Time as Time
25−import Data.Traversable (traverse)
26−
15+import Control.Alt ((<|>))
16+import Control.Monad.Except (ExceptT(..), runExceptT)
17+import Data.Date as Date
18+import Data.DateTime (DateTime(..), Time(..))
19+import Data.Either (Either(..), either, isRight)
20+import Data.Enum (fromEnum, toEnum)
21+import Foreign as Foreign
22+import Foreign (Foreign, F)
23+import Data.Function.Uncurried as Fn
24+import Data.Function.Uncurried (Fn2, Fn4, Fn7)
25+import Data.Identity (Identity(..))
26+import Data.List.NonEmpty (NonEmptyList(..))
27+import Data.List.Types (List(..))
28+import Data.Maybe (Maybe(..))
29+import Data.NonEmpty (NonEmpty(..))
30+import Data.Nullable (Nullable, toNullable)
31+import Data.Time as Time
32+import Data.Traversable (traverse)
2733
2834newtype Key = Key Foreign
2935
30−
31−extractForeign :: Key -> Foreign
32−extractForeign (Key f) =
33− f
34−
35−
3636--------------------
3737-- INTERFACES
3838--
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).
3942class IDBKey a where
40− toKey :: a -> Key
41− fromKey :: Key -> F a
43+ toKey :: a -> Key
44+ fromKey :: Key -> F a
4245 unsafeFromKey :: Key -> a
4346
47+none :: Maybe Key
48+none =
49+ Nothing
4450
4551--------------------
4652-- INSTANCES
4753--
4854instance eqKey :: Eq Key where
4955 eq a b = (runExceptT >>> runIdentity >>> isRight) $
50− eq <$> ((fromKey a) :: F Int) <*> fromKey b
51− <|>
52− eq <$> ((fromKey a) :: F Number) <*> fromKey b
53− <|>
54− eq <$> ((fromKey a) :: F String) <*> fromKey b
55− <|>
56− eq <$> ((fromKey a) :: F DateTime) <*> fromKey b
57− <|>
58− eq <$> ((fromKey a) :: F (Array Key)) <*> fromKey b
56+ eq <$> ((fromKey a) :: F Int) <*> fromKey b
57+ <|>
58+ eq <$> ((fromKey a) :: F Number) <*> fromKey b
59+ <|>
60+ eq <$> ((fromKey a) :: F String) <*> fromKey b
61+ <|>
62+ eq <$> ((fromKey a) :: F DateTime) <*> fromKey b
63+ <|>
64+ eq <$> ((fromKey a) :: F (Array Key)) <*> fromKey b
5965 where
60− runIdentity :: forall a. Identity a -> a
61− runIdentity (Identity x) = x
62−
66+ runIdentity :: forall a. Identity a -> a
67+ runIdentity (Identity x) = x
6368
6469instance ordKey :: Ord Key where
65− compare a b = (runExceptT >>> runIdentity >>> either (const LT) id) $
66− compare <$> ((fromKey a) :: F Int) <*> fromKey b
67− <|>
68− compare <$> ((fromKey a) :: F Number) <*> fromKey b
69− <|>
70− compare <$> ((fromKey a) :: F String) <*> fromKey b
71− <|>
72− compare <$> ((fromKey a) :: F DateTime) <*> fromKey b
73− <|>
74− compare <$> ((fromKey a) :: F (Array Key)) <*> fromKey b
70+ compare a b = (runExceptT >>> runIdentity >>> either (const LT) identity) $
71+ compare <$> ((fromKey a) :: F Int) <*> fromKey b
72+ <|>
73+ compare <$> ((fromKey a) :: F Number) <*> fromKey b
74+ <|>
75+ compare <$> ((fromKey a) :: F String) <*> fromKey b
76+ <|>
77+ compare <$> ((fromKey a) :: F DateTime) <*> fromKey b
78+ <|>
79+ compare <$> ((fromKey a) :: F (Array Key)) <*> fromKey b
7580 where
76− runIdentity :: forall a. Identity a -> a
77− runIdentity (Identity x) = x
78−
81+ runIdentity :: forall a. Identity a -> a
82+ runIdentity (Identity x) = x
7983
8084instance showKey :: Show Key where
8185 show a = (runExceptT >>> format) $
82− (show <$> (fromKey a :: F Int))
83− <|>
84− (show <$> (fromKey a :: F Number))
85− <|>
86− (show <$> (fromKey a :: F String))
87− <|>
88− (show <$> (fromKey a :: F DateTime))
89− <|>
90− (show <$> (fromKey a :: F (Array Key)))
86+ (show <$> (fromKey a :: F Int))
87+ <|>
88+ (show <$> (fromKey a :: F Number))
89+ <|>
90+ (show <$> (fromKey a :: F String))
91+ <|>
92+ (show <$> (fromKey a :: F DateTime))
93+ <|>
94+ (show <$> (fromKey a :: F (Array Key)))
9195 where
92− format :: forall a. Identity (Either a String) -> String
93− format (Identity x) =
94− either (const "(Key)") (\s -> "(Key " <> s <> ")") x
95−
96+ format :: forall a. Identity (Either a String) -> String
97+ format (Identity x) =
98+ either (const "(Key)") (\s -> "(Key " <> s <> ")") x
9699
97100instance idbKeyKey :: IDBKey Key where
98− toKey = id
99− fromKey = pure
100− unsafeFromKey = id
101+ toKey = identity
102+ fromKey = pure
103+ unsafeFromKey = identity
101104
105+instance idbKeyForeign :: IDBKey Foreign where
106+ toKey = Key
107+ fromKey (Key f) = pure f
108+ unsafeFromKey (Key f) = f
102109
103110instance idbKeyInt :: IDBKey Int where
104− toKey = Foreign.toForeign >>> Key
105− fromKey (Key f) = Foreign.readInt f
111+ toKey = Foreign.unsafeToForeign >>> Key
112+ fromKey (Key f) = Foreign.readInt f
106113 unsafeFromKey (Key f) = Foreign.unsafeFromForeign f
107114
108−
109115instance idbKeyNumber :: IDBKey Number where
110− toKey = Foreign.toForeign >>> Key
111− fromKey (Key f) = Foreign.readNumber f
116+ toKey = Foreign.unsafeToForeign >>> Key
117+ fromKey (Key f) = Foreign.readNumber f
112118 unsafeFromKey (Key f) = Foreign.unsafeFromForeign f
113119
114−
115120instance idbKeyString :: IDBKey String where
116− toKey = Foreign.toForeign >>> Key
117− fromKey (Key f) = Foreign.readString f
121+ toKey = Foreign.unsafeToForeign >>> Key
122+ fromKey (Key f) = Foreign.readString f
118123 unsafeFromKey (Key f) = Foreign.unsafeFromForeign f
119124
120−
121125instance idbKeyDate :: IDBKey DateTime where
122− toKey (DateTime d t) = Key $ Fn.runFn7 _dateTimeToForeign
126+ toKey (DateTime d t) = Key $ Fn.runFn7 _dateTimeToForeign
123127 (fromEnum $ Date.year d)
124128 (fromEnum $ Date.month d)
125129 (fromEnum $ Date.day d)
@@ -127,40 +131,51 @@ instance idbKeyDate :: IDBKey DateTime where
127131 (fromEnum $ Time.minute t)
128132 (fromEnum $ Time.second t)
129133 (fromEnum $ Time.millisecond t)
130− fromKey (Key f) = Fn.runFn4 _readDateTime dateTime dateTimeF dateTimeE f
134+ fromKey (Key f) = Fn.runFn4 _readDateTime dateTime dateTimeF dateTimeE f
131135 unsafeFromKey (Key f) = Fn.runFn2 _unsafeReadDateTime dateTime f
132136
133−
134137instance idbKeyArray :: IDBKey a => IDBKey (Array a) where
135− toKey = Foreign.toForeign >>> Key
136− fromKey (Key f) = Foreign.readArray f >>= traverse (Key >>> fromKey)
138+ toKey = Foreign.unsafeToForeign >>> Key
139+ fromKey (Key f) = Foreign.readArray f >>= traverse (Key >>> fromKey)
137140 unsafeFromKey (Key f) = map unsafeFromKey (Foreign.unsafeFromForeign f)
138141
139−
140−dateTime :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> Nullable DateTime
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
151+ -> Nullable DateTime
141152dateTime y m d h mi s ms =
142153 toNullable $ DateTime
143− <$> (Date.canonicalDate <$> toEnum y <*> toEnum m <*> toEnum d)
144− <*> (Time <$> toEnum h <*> toEnum mi <*> toEnum s <*> toEnum ms)
145−
154+ <$> (Date.canonicalDate <$> toEnum y <*> toEnum m <*> toEnum d)
155+ <*> (Time <$> toEnum h <*> toEnum mi <*> toEnum s <*> toEnum ms)
146156
147−dateTimeF :: DateTime -> F DateTime
157+-- FFI constructor to convert a JS `Date` into a successful `F DateTime`
158+dateTimeF
159+ :: DateTime
160+ -> F DateTime
148161dateTimeF =
149162 Right >>> Identity >>> ExceptT
150163
151−
152−dateTimeE :: String -> F DateTime
164+-- FFI constructor to convert a string into an errored `F DateTime`
165+dateTimeE
166+ :: String
167+ -> F DateTime
153168dateTimeE =
154169 Foreign.TypeMismatch "Date" >>> flip NonEmpty Nil >>> NonEmptyList >>> Left >>> Identity >>> ExceptT
155170
156−
157171--------------------
158172-- FFI
159173--
160−foreign import _dateTimeToForeign :: Fn7 Int Int Int Int Int Int Int Foreign
161−
162−
163−foreign import _readDateTime :: Fn4 (Int -> Int -> Int -> Int -> Int -> Int -> Int -> Nullable DateTime) (DateTime -> F DateTime) (String -> F DateTime) Foreign (F DateTime)
174+foreign import _dateTimeToForeign
175+ :: Fn7 Int Int Int Int Int Int Int Foreign
164176
177+foreign import _readDateTime
178+ :: Fn4 (Int -> Int -> Int -> Int -> Int -> Int -> Int -> Nullable DateTime) (DateTime -> F DateTime) (String -> F DateTime) Foreign (F DateTime)
165179
166−foreign import _unsafeReadDateTime :: Fn2 (Int -> Int -> Int -> Int -> Int -> Int -> Int -> Nullable DateTime) Foreign DateTime
180+foreign import _unsafeReadDateTime
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) {
22 try {
33 return IDBKeyRange.bound(lower, upper, lowerOpen, upperOpen);
44 } catch (e) {
@@ -6,34 +6,34 @@ exports._bound = function _bound(lower, upper, lowerOpen, upperOpen) {
66 }
77};
88
9−exports._includes = function _includes(range, key) {
9+export function _includes(range, key) {
1010 return range.includes(key);
1111};
1212
13−exports._lower = function _lower(range) {
13+export function _lower(range) {
1414 return range.lower;
1515};
1616
17−exports._lowerBound = function _lowerBound(lower, open) {
17+export function _lowerBound(lower, open) {
1818 return IDBKeyRange.lowerBound(lower, open);
1919};
2020
21−exports._lowerOpen = function _lowerOpen(range) {
21+export function _lowerOpen(range) {
2222 return range.lowerOpen;
2323};
2424
25−exports._only = function _only(key) {
25+export function _only(key) {
2626 return IDBKeyRange.only(key);
2727};
2828
29−exports._upper = function _upper(range) {
29+export function _upper(range) {
3030 return range.upper;
3131};
3232
33−exports._upperBound = function _upperBound(upper, open) {
33+export function _upperBound(upper, open) {
3434 return IDBKeyRange.upperBound(upper, open);
3535};
3636
37−exports._upperOpen = function _upperOpen(range) {
37+export function _upperOpen(range) {
3838 return range.upperOpen;
3939};
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.
12module Database.IndexedDB.IDBKeyRange
2− ( class IDBKeyRange, includes
3+ -- * Types
4+ ( Open
5+
6+ -- * Constructors
37 , only
48 , lowerBound
59 , upperBound
610 , bound
11+
12+ -- * Interface
13+ , includes
14+
15+ -- * Attributes
716 , lower
817 , upper
918 , lowerOpen
1019 , upperOpen
1120 ) where
1221
13−import Prelude
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)
22+import Prelude (($), (>>>), map)
2023
21−import Database.IndexedDB.Core (KeyRange)
22−import Database.IndexedDB.IDBKey.Internal (class IDBKey, Key(..), toKey, extractForeign)
24+import Foreign (Foreign)
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)
2329
30+import Database.IndexedDB.Core (class IDBKeyRange, KeyRange)
31+import Database.IndexedDB.IDBKey.Internal (class IDBKey, Key, toKey, unsafeFromKey)
2432
2533--------------------
26−-- INTERFACES
34+-- TYPES
2735--
28−class IDBKeyRange range where
29− includes :: range -> Key -> Boolean
3036
37+-- | Type alias for open
38+type Open = Boolean
3139
32−only :: forall a. (IDBKey a) => a -> KeyRange
33−only key =
34− _only (extractForeign $ toKey key)
35−
40+--------------------
41+-- CONSTRUCTORS
42+--
3643
37−lowerBound :: forall a. (IDBKey a) => a -> Boolean -> KeyRange
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.
55+lowerBound
56+ :: forall a
57+ . (IDBKey a)
58+ => a
59+ -> Open
60+ -> KeyRange
3861lowerBound key open =
39− Fn.runFn2 _lowerBound (extractForeign $ toKey key) open
40−
41−
42−upperBound :: forall a. (IDBKey a) => a -> Boolean -> KeyRange
62+ Fn.runFn2 _lowerBound (unsafeFromKey $ toKey key) open
63+
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.
66+upperBound
67+ :: forall a
68+ . (IDBKey a)
69+ => a
70+ -> Open
71+ -> KeyRange
4372upperBound key open =
44− Fn.runFn2 _upperBound (extractForeign $ toKey key) open
45−
46−bound :: forall a. (IDBKey a) => { lower :: a, upper :: a, lowerOpen :: Boolean, upperOpen :: Boolean } -> Maybe KeyRange
73+ Fn.runFn2 _upperBound (unsafeFromKey $ toKey key) open
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)
83+ => { lower :: key, upper :: key, lowerOpen :: Boolean, upperOpen :: Boolean }
84+ -> Maybe KeyRange
4785bound { lower: key1, upper: key2, lowerOpen: open1, upperOpen: open2 } =
4886 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+--
5092
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
51103
52104--------------------
53105-- ATTRIBUTES
54106--
55−lower :: KeyRange -> Maybe Key
107+-- | Returns lower bound if any.
108+lower
109+ :: KeyRange
110+ -> Maybe Key
56111lower =
57− _lower >>> toMaybe >>> map Key
112+ _lower >>> toMaybe >>> map toKey
58113
59−
60−upper :: KeyRange -> Maybe Key
114+-- | Returns upper bound if any.
115+upper
116+ :: KeyRange
117+ -> Maybe Key
61118upper =
62− _upper >>> toMaybe >>> map Key
63−
119+ _upper >>> toMaybe >>> map toKey
64120
65−lowerOpen :: KeyRange -> Boolean
121+-- | Returns true if the lower open flag is set, false otherwise.
122+lowerOpen
123+ :: KeyRange
124+ -> Boolean
66125lowerOpen =
67126 _lowerOpen
68127
69−
70−upperOpen :: KeyRange -> Boolean
128+-- | Returns true if the upper open flag is set, false otherwise.
129+upperOpen
130+ :: KeyRange
131+ -> Boolean
71132upperOpen =
72133 _upperOpen
73134
74−
75−--------------------
76−-- INSTANCES
77−--
78−instance idbKeyRangeKeyRange :: IDBKeyRange KeyRange where
79− includes range =
80− extractForeign >>> Fn.runFn2 _includes range
81−
82−
83135--------------------
84136-- FFI
85137--
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−
97138
98−foreign import _lowerOpen :: KeyRange -> Boolean
139+foreign import _only
140+ :: Foreign
141+ -> KeyRange
99142
143+foreign import _lowerBound
144+ :: Fn2 Foreign Boolean KeyRange
100145
101−foreign import _only :: Foreign -> KeyRange
146+foreign import _upperBound
147+ :: Fn2 Foreign Boolean KeyRange
102148
149+foreign import _bound
150+ :: Fn4 Foreign Foreign Boolean Boolean (Nullable KeyRange)
103151
104−foreign import _upper :: KeyRange -> Nullable Foreign
152+foreign import _includes
153+ :: forall range
154+ . Fn2 range Foreign Boolean
105155
156+foreign import _lower
157+ :: KeyRange
158+ -> Nullable Foreign
106159
107−foreign import _upperBound :: Fn2 Foreign Boolean KeyRange
160+foreign import _upper
161+ :: KeyRange
162+ -> Nullable Foreign
108163
164+foreign import _lowerOpen
165+ :: KeyRange
166+ -> Boolean
109167
110−foreign import _upperOpen :: KeyRange -> Boolean
168+foreign import _upperOpen
169+ :: KeyRange
170+ -> Boolean
src/Database/IndexedDB/IDBObjectStore.js+55−28
@@ -1,6 +1,6 @@
11const errorHandler = function errorHandler(cb) {
22 return function _handler(e) {
3− cb(new Error(e.target.error.name));
3+ cb(e.target.error);
44 };
55};
66
@@ -15,36 +15,44 @@ const toArray = function toArray(xs) {
1515};
1616
1717
18−exports._add = function _add(store, value, key) {
19− return function aff(success, error) {
18+export function _add(store, value, key) {
19+ return function aff(error, success) {
2020 try {
2121 const request = store.add(value, key || undefined);
2222 request.onsuccess = successHandler(success);
2323 request.onerror = errorHandler(error);
2424 } catch (e) {
25− error(new Error(e.name));
25+ error(e);
2626 }
27+
28+ return function canceler(_, cancelerError) {
29+ cancelerError(new Error("Can't cancel IDB Effects"));
30+ };
2731 };
2832};
2933
30−exports._autoIncrement = function _autoIncrement(store) {
34+export function _autoIncrement(store) {
3135 return store.autoIncrement;
3236};
3337
34−exports._clear = function _clear(store) {
35− return function aff(success, error) {
38+export function _clear(store) {
39+ return function aff(error, success) {
3640 try {
3741 const request = store.clear();
3842 request.onsuccess = successHandler(success);
3943 request.onerror = errorHandler(error);
4044 } catch (e) {
41− error(new Error(e.name));
45+ error(e);
4246 }
47+
48+ return function canceler(_, cancelerError) {
49+ cancelerError(new Error("Can't cancel IDB Effects"));
50+ };
4351 };
4452};
4553
46−exports._createIndex = function _createIndex(store, name, path, params) {
47− return function aff(success, error) {
54+export function _createIndex(store, name, path, params) {
55+ return function aff(error, success) {
4856 var keyPath;
4957
5058 try {
@@ -64,50 +72,66 @@ exports._createIndex = function _createIndex(store, name, path, params) {
6472 const index = store.createIndex(name, keyPath, params);
6573 success(index);
6674 } catch (e) {
67− error(new Error(e.name));
75+ error(e);
6876 }
77+
78+ return function canceler(_, cancelerError) {
79+ cancelerError(new Error("Can't cancel IDB Effects"));
80+ };
6981 };
7082};
7183
72−exports._deleteIndex = function _deleteIndex(store, name) {
73− return function aff(success, error) {
84+export function _deleteIndex(store, name) {
85+ return function aff(error, success) {
7486 try {
7587 store.deleteIndex(name);
7688 success();
7789 } catch (e) {
78− error(new Error(e.name));
90+ error(e);
7991 }
92+
93+ return function canceler(_, cancelerError) {
94+ cancelerError(new Error("Can't cancel IDB Effects"));
95+ };
8096 };
8197};
8298
83−exports._delete = function _delete(store, query) {
84− return function aff(success, error) {
99+export function _delete(store, query) {
100+ return function aff(error, success) {
85101 try {
86102 const request = store.delete(query);
87103 request.onsuccess = successHandler(success);
88104 request.onerror = errorHandler(error);
89105 } catch (e) {
90− error(new Error(e.name));
106+ error(e);
91107 }
108+
109+ return function canceler(_, cancelerError) {
110+ cancelerError(new Error("Can't cancel IDB Effects"));
111+ };
92112 };
93113};
94114
95−exports._index = function _index(store, name) {
96− return function aff(success, error) {
115+export function _index(store, name) {
116+ return function aff(error, success) {
97117 try {
98118 const index = store.index(name);
99119 success(index);
100120 } catch (e) {
101− error(new Error(e.name));
121+ error(e);
102122 }
123+
124+ return function canceler(_, cancelerError) {
125+ cancelerError(new Error("Can't cancel IDB Effects"));
126+ };
103127 };
104128};
105129
106−exports._indexNames = function _indexNames(store) {
130+export function _indexNames(store) {
107131 return toArray(store.indexNames);
108132};
109133
110−exports._keyPath = function _keyPath(store) {
134+export function _keyPath(store) {
111135 const path = store.keyPath;
112136
113137 if (Array.isArray(path)) {
@@ -121,23 +145,26 @@ exports._keyPath = function _keyPath(store) {
121145 return [];
122146};
123147
124−exports._name = function _name(store) {
148+export function _name(store) {
125149 return store.name;
126150};
127151
128−exports._put = function _put(store, value, key) {
129− return function aff(success, error) {
152+export function _put(store, value, key) {
153+ return function aff(error, success) {
130154 try {
131155 const request = store.put(value, key || undefined);
132156 request.onsuccess = successHandler(success);
133157 request.onerror = errorHandler(error);
134158 } catch (e) {
135− error(new Error(e.name));
159+ error(e);
136160 }
161+
162+ return function canceler(_, cancelerError) {
163+ cancelerError(new Error("Can't cancel IDB Effects"));
164+ };
137165 };
138166};
139167
140−exports._transaction = function _transaction(store) {
168+export function _transaction(store) {
141169 return store.transaction;
142170};
143−
src/Database/IndexedDB/IDBObjectStore.purs+217−107
@@ -1,147 +1,257 @@
1+-- | An object store is the primary storage mechanism for storing data in a database.
12module Database.IndexedDB.IDBObjectStore
2− ( class IDBObjectStore, add, clear, createIndex, delete, deleteIndex, index, put
3− , module Database.IndexedDB.IDBIndex.Internal
4− , IDBObjectStoreParameters
5− , IndexName
3+ -- * Types
4+ ( IndexName
5+ , IndexParameters
6+ , defaultParameters
7+
8+ -- * Interface
9+ , add
10+ , clear
11+ , createIndex
12+ , delete
13+ , deleteIndex
14+ , index
15+ , put
16+
17+ -- * Attributes
618 , autoIncrement
719 , indexNames
820 , keyPath
921 , name
1022 , transaction
11− , defaultParameters
12− ) where
13−
14−import Prelude (Unit, ($), (<$>), (>>>))
1523
16−import Control.Monad.Aff (Aff)
17−import Data.Foreign (Foreign)
18−import Data.Function.Uncurried as Fn
19−import Data.Function.Uncurried (Fn2, Fn3, Fn4)
20−import Data.Maybe (Maybe)
21−import Data.Nullable (Nullable, toNullable)
24+ -- * Re-Exports
25+ , module Database.IndexedDB.IDBIndex
26+ ) where
2227
23−import Database.IndexedDB.Core (IDB, Index, KeyRange, KeyPath, ObjectStore, Transaction)
24−import Database.IndexedDB.IDBIndex.Internal (class IDBIndex, IDBIndexParameters, count, get, getAllKeys, getKey, openCursor, openKeyCursor)
25−import Database.IndexedDB.IDBKey.Internal (class IDBKey, Key(Key), extractForeign, toKey)
28+import Database.IndexedDB.Core
2629
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, ($), (<$>), (>>>), (<<<))
2740
2841--------------------
29−-- INTERFACES
42+-- TYPES
3043--
31−class IDBObjectStore store where
32− add :: forall v k e. (IDBKey k) => store -> v -> Maybe k -> Aff (idb :: IDB | e) Key
33− clear :: forall e. store -> Aff (idb :: IDB | e) Unit
34− createIndex :: forall e. store -> IndexName -> KeyPath -> IDBIndexParameters -> Aff (idb :: IDB | e) Index
35− delete :: forall e. store -> KeyRange -> Aff (idb :: IDB | e) Unit
36− deleteIndex :: forall e. store -> IndexName -> Aff (idb :: IDB | e) Unit
37− index :: forall e. store -> IndexName -> Aff (idb :: IDB | e) Index
38− put :: forall v k e. (IDBKey k) => store -> v -> Maybe k -> Aff (idb :: IDB | e) Key
39−
4044
45+-- | Type alias for IndexName
4146type IndexName = String
4247
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+ }
4365
44−type IDBObjectStoreParameters =
45− { keyPath :: KeyPath
46− , autoIncrement :: Boolean
66+defaultParameters :: IndexParameters
67+defaultParameters =
68+ { unique: false
69+ , multiEntry: false
4770 }
4871
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)
49168
50169--------------------
51170-- ATTRIBUTES
52171--
53−autoIncrement :: ObjectStore -> Boolean
172+-- | Returns `true` if the store has a key generator, and `false` otherwise.
173+autoIncrement
174+ :: ObjectStore
175+ -> Boolean
54176autoIncrement =
55177 _autoIncrement
56178
57−
58−indexNames :: ObjectStore -> Array String
179+--| Returns a list of the names of indexes in the store.
180+indexNames
181+ :: ObjectStore
182+ -> Array String
59183indexNames =
60184 _indexNames
61185
62−
63−keyPath :: ObjectStore -> Array String
186+-- | Returns the key path of the store, or empty array if none
187+keyPath
188+ :: ObjectStore
189+ -> Array String
64190keyPath =
65191 _keyPath
66192
67−
68−name :: ObjectStore -> String
193+-- | Returns the name of the store.
194+name
195+ :: ObjectStore
196+ -> String
69197name =
70198 _name
71199
72−
73−transaction :: ObjectStore -> Transaction
200+-- | Returns the associated transaction.
201+transaction
202+ :: ObjectStore
203+ -> Transaction
74204transaction =
75205 _transaction
76206
77−
78−--------------------
79−-- INSTANCES
80−--
81−instance idbObjectStoreObjectStore :: IDBObjectStore ObjectStore where
82− add store value key =
83− Key <$> Fn.runFn3 _add store value (toNullable $ (toKey >>> extractForeign) <$> key)
84−
85− clear =
86− _clear
87−
88− createIndex store name' path params =
89− Fn.runFn4 _createIndex store name' path params
90−
91− delete store range =
92− Fn.runFn2 _delete store range
93−
94− deleteIndex store name' =
95− Fn.runFn2 _deleteIndex store name'
96−
97− index store name' =
98− Fn.runFn2 _index store name'
99−
100− put store value key =
101− Key <$> Fn.runFn3 _put store value (toNullable $ (toKey >>> extractForeign) <$> key)
102−
103−
104−defaultParameters :: IDBObjectStoreParameters
105−defaultParameters =
106− { keyPath : []
107− , autoIncrement : false
108− }
109−
110−
111207--------------------
112208-- FFI
113209--
114−foreign import _add :: forall value e. Fn3 ObjectStore value (Nullable Foreign) (Aff (idb :: IDB | e) Foreign)
115−
116−
117−foreign import _autoIncrement :: ObjectStore -> Boolean
118−
119−
120−foreign import _clear :: forall e. ObjectStore -> Aff (idb :: IDB | e) Unit
121−
122−
123−foreign import _createIndex :: forall e. Fn4 ObjectStore String (Array String) { unique :: Boolean, multiEntry :: Boolean } (Aff (idb :: IDB | e) Index)
124−
125−
126−foreign import _delete :: forall e. Fn2 ObjectStore KeyRange (Aff (idb :: IDB | e) Unit)
127−
128−
129−foreign import _deleteIndex :: forall e. Fn2 ObjectStore String (Aff (idb :: IDB | e) Unit)
130−
131−
132−foreign import _index :: forall e. Fn2 ObjectStore String (Aff (idb :: IDB | e) Index)
133−
134−
135−foreign import _indexNames :: ObjectStore -> Array String
136−
137−
138−foreign import _keyPath :: ObjectStore -> Array String
139−
140−
141−foreign import _name :: ObjectStore -> String
142−
143−
144−foreign import _put :: forall value e. Fn3 ObjectStore value (Nullable Foreign) (Aff (idb :: IDB | e) Foreign)
145−
146−
147−foreign import _transaction :: ObjectStore -> Transaction
210+foreign import _add
211+ :: forall val store
212+ . Fn3 store val (Nullable Foreign) (EffectFnAff Foreign)
213+
214+foreign import _autoIncrement
215+ :: ObjectStore
216+ -> Boolean
217+
218+foreign import _clear
219+ :: forall store
220+ . store
221+ -> EffectFnAff Unit
222+
223+foreign import _createIndex
224+ :: forall store
225+ . Fn4 store String (Array String) { unique :: Boolean, multiEntry :: Boolean } (EffectFnAff Index)
226+
227+foreign import _delete
228+ :: forall store
229+ . Fn2 store KeyRange (EffectFnAff Unit)
230+
231+foreign import _deleteIndex
232+ :: forall store
233+ . Fn2 store String (EffectFnAff Unit)
234+
235+foreign import _index
236+ :: forall store
237+ . Fn2 store String (EffectFnAff Index)
238+
239+foreign import _indexNames
240+ :: ObjectStore
241+ -> Array String
242+
243+foreign import _keyPath
244+ :: ObjectStore
245+ -> Array String
246+
247+foreign import _name
248+ :: ObjectStore
249+ -> String
250+
251+foreign import _put
252+ :: forall val store
253+ . Fn3 store val (Nullable Foreign) (EffectFnAff Foreign)
254+
255+foreign import _transaction
256+ :: ObjectStore
257+ -> Transaction
src/Database/IndexedDB/IDBTransaction.js+44−16
@@ -1,21 +1,29 @@
1−exports._abort = function _abort(tx) {
2− return function aff(success, error) {
1+export function _abort(tx) {
2+ return function aff(error, success) {
33 try {
44 tx.abort();
55 success();
66 } catch (e) {
7− error(new Error(e.name));
7+ error(e);
88 }
9+
10+ return function canceler(_, cancelerError) {
11+ cancelerError(new Error("Can't cancel IDB Effects"));
12+ };
913 };
1014};
1115
12−exports._error = function _error(tx) {
16+export function _db(tx) {
17+ return tx.db;
18+};
19+
20+export function _error(tx) {
1321 return tx.error == null
1422 ? null
15− : new Error(tx.error.name);
23+ : tx.error;
1624};
1725
18−exports._mode = function _mode(ReadOnly, ReadWrite, VersionChange, tx) {
26+export function _mode(ReadOnly, ReadWrite, VersionChange, tx) {
1927 if (tx.mode === 'readwrite') {
2028 return ReadWrite;
2129 } else if (tx.mode === 'versionchange') {
@@ -25,40 +33,60 @@ exports._mode = function _mode(ReadOnly, ReadWrite, VersionChange, tx) {
2533 return ReadOnly;
2634};
2735
28−exports._objectStore = function _objectStore(tx, name) {
29− return function aff(success, error) {
36+export function _objectStore(tx, name) {
37+ return function aff(error, success) {
3038 try {
3139 const store = tx.objectStore(name);
3240 success(store);
3341 } catch (e) {
34− error(new Error(e.name));
42+ error(e);
3543 }
44+
45+ return function canceler(_, cancelerError) {
46+ cancelerError(new Error("Can't cancel IDB Effects"));
47+ };
3648 };
3749};
3850
39−exports._onAbort = function _onAbort(tx, f) {
40− return function aff(success) {
51+export function _objectStoreNames(tx) {
52+ return tx.objectStoreNames;
53+};
54+
55+export function _onAbort(tx, f) {
56+ return function aff(error, success) {
4157 tx.onabort = function onabort() {
4258 f();
4359 };
4460 success();
61+
62+ return function canceler(_, cancelerError) {
63+ cancelerError(new Error("Can't cancel IDB Effects"));
64+ };
4565 };
4666};
4767
48−exports._onComplete = function _onComplete(tx, f) {
49− return function aff(success) {
68+export function _onComplete(tx, f) {
69+ return function aff(error, success) {
5070 tx.oncomplete = function oncomplete() {
5171 f();
5272 };
5373 success();
74+
75+ return function canceler(_, cancelerError) {
76+ cancelerError(new Error("Can't cancel IDB Effects"));
77+ };
5478 };
5579};
5680
57−exports._onError = function _onError(tx, f) {
58− return function aff(success) {
81+export function _onError(tx, f) {
82+ return function aff(error, success) {
5983 tx.onerror = function onerror(e) {
60− f(new Error(e.target.error.name))();
84+ f(e.target.error)();
6185 };
6286 success();
87+
88+ return function canceler(_, cancelerError) {
89+ cancelerError(new Error("Can't cancel IDB Effects"));
90+ };
6391 };
6492};
src/Database/IndexedDB/IDBTransaction.purs+111−52
@@ -1,94 +1,153 @@
1+-- | An object store is the primary storage mechanism for storing data in a database.
12module Database.IndexedDB.IDBTransaction
2− (class IDBTransaction, abort, objectStore
3+ -- * Interface
4+ ( abort
5+ , objectStore
6+
7+ -- * Attributes
38 , error
49 , mode
10+
11+ -- * Event Handlers
512 , onAbort
613 , onComplete
714 , onError
815 ) where
916
10−import Prelude (Unit, (>>>))
11−
12−import Control.Monad.Aff (Aff)
13−import Control.Monad.Eff (Eff)
14−import Control.Monad.Eff.Exception (Error)
15−import Data.Function.Uncurried as Fn
16−import Data.Function.Uncurried (Fn2, Fn4)
17−import Data.Maybe (Maybe)
18−import Data.Nullable (Nullable, toMaybe)
19−
20−import Database.IndexedDB.Core (IDB, ObjectStore, Transaction, TransactionMode(..))
17+import Database.IndexedDB.Core
2118
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, ($), (<<<), (>>>))
2228
2329--------------------
2430-- INTERFACES
2531--
26−class IDBTransaction tx where
27− abort :: forall e. tx -> Aff (idb :: IDB | e) Unit
28− objectStore :: forall e. tx -> String -> Aff (idb :: IDB | e) ObjectStore
2932
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
3052
3153--------------------
3254-- ATTRIBUTES
3355--
34−error :: Transaction -> Maybe Error
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
66+ :: Transaction
67+ -> Maybe Error
3568error =
3669 _error >>> toMaybe
3770
38−
39−mode :: Transaction -> TransactionMode
71+-- | Returns the mode the transaction was created with (`ReadOnly|ReadWrite`)
72+-- | , or `VersionChange` for an upgrade transaction.
73+mode
74+ :: Transaction
75+ -> TransactionMode
4076mode =
4177 Fn.runFn4 _mode ReadOnly ReadWrite VersionChange
4278
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
4386
4487--------------------
4588-- EVENT HANDLERS
4689--
47−onAbort :: forall e e'. Transaction -> Eff ( | e') Unit -> Aff (idb :: IDB | e) Unit
48−onAbort db f =
49− Fn.runFn2 _onAbort db f
50−
51−
52−onComplete :: forall e e'. Transaction -> Eff ( | e') Unit -> Aff (idb :: IDB | e) Unit
53−onComplete db f =
54− Fn.runFn2 _onComplete db f
55−
56−
57−onError :: forall e e'. Transaction -> (Error -> Eff ( | e') Unit) -> Aff (idb :: IDB | e) Unit
58−onError db f =
59− Fn.runFn2 _onError db f
60−
61−
62−--------------------
63−-- INSTANCES
64−--
65−instance idbTransactionTransaction :: IDBTransaction Transaction where
66− objectStore tx name =
67− Fn.runFn2 _objectStore tx name
68−
69− abort =
70− _abort
7190
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
72114
73115--------------------
74116-- FFI
75117--
76−foreign import _abort :: forall tx e. tx -> Aff (idb :: IDB | e) Unit
77−
78−
79−foreign import _error :: Transaction -> (Nullable Error)
80−
81118
82−foreign import _mode :: Fn4 TransactionMode TransactionMode TransactionMode Transaction TransactionMode
119+foreign import _abort
120+ :: forall tx
121+ . tx
122+ -> EffectFnAff Unit
83123
124+foreign import _db
125+ :: Transaction
126+ -> Database
84127
85−foreign import _objectStore :: forall tx e. Fn2 tx String (Aff (idb :: IDB | e) ObjectStore)
128+foreign import _error
129+ :: Transaction
130+ -> (Nullable Error)
86131
132+foreign import _mode
133+ :: Fn4 TransactionMode TransactionMode TransactionMode Transaction TransactionMode
87134
88−foreign import _onAbort :: forall tx e e'. Fn2 tx (Eff ( | e') Unit) (Aff (idb :: IDB | e) Unit)
135+foreign import _objectStoreNames
136+ :: Transaction
137+ -> Array String
89138
139+foreign import _objectStore
140+ :: forall tx
141+ . Fn2 tx String (EffectFnAff ObjectStore)
90142
91−foreign import _onComplete :: forall tx e e'. Fn2 tx (Eff ( | e') Unit) (Aff (idb :: IDB | e) Unit)
143+foreign import _onAbort
144+ :: forall tx
145+ . Fn2 tx (Effect Unit) (EffectFnAff Unit)
92146
147+foreign import _onComplete
148+ :: forall tx
149+ . Fn2 tx (Effect Unit) (EffectFnAff Unit)
93150
94−foreign import _onError :: forall tx e e'. Fn2 tx (Error -> Eff ( | e') Unit) (Aff (idb :: IDB | e) Unit)
151+foreign import _onError
152+ :: forall tx
153+ . Fn2 tx (Error -> Effect Unit) (EffectFnAff 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.only : describe;
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+1060−710
@@ -2,790 +2,1140 @@ module Test.Main where
22
33import Prelude
44
5−import Control.Monad.Aff (Aff, launchAff, forkAff, delay, attempt)
6−import Control.Monad.Aff.AVar (AVAR, makeVar, makeVar', modifyVar, peekVar, putVar, takeVar)
7−import Control.Monad.Aff.Console (log)
8−import Control.Monad.Eff (Eff)
9−import Control.Monad.Eff.Class (liftEff)
10−import Control.Monad.Eff.Exception (EXCEPTION, message)
11−import Control.Monad.Eff.Now (NOW, now)
12−import Data.Date as Date
13−import Data.DateTime as DateTime
14−import Data.DateTime (DateTime(..), Time(..))
15−import Data.DateTime.Instant (toDateTime)
16−import Data.Either (Either(..))
17−import Data.Enum (toEnum)
18−import Data.Maybe (Maybe(..), isNothing, maybe)
19−import Data.Time.Duration (Milliseconds(..))
20−import Data.Traversable (traverse)
21−import Data.Tuple (Tuple(..), uncurry)
22−import Test.Spec (describe, describe, it, it)
23−import Test.Spec.Assertions (shouldEqual, fail)
24−import Test.Spec.Mocha (MOCHA, runMocha)
25−
26−import Database.IndexedDB.Core
27−import Database.IndexedDB.IDBKey
28−import Database.IndexedDB.IDBCursor as IDBCursor
29−import Database.IndexedDB.IDBDatabase as IDBDatabase
30−import Database.IndexedDB.IDBFactory as IDBFactory
31−import Database.IndexedDB.IDBIndex as IDBIndex
32−import Database.IndexedDB.IDBKeyRange as IDBKeyRange
33−import Database.IndexedDB.IDBObjectStore as IDBObjectStore
34−import Database.IndexedDB.IDBTransaction as IDBTransaction
35−
5+import Data.Array (head, drop)
6+import Data.Date as Date
7+import Data.DateTime (DateTime(..), Time(..))
8+import Data.DateTime.Instant (toDateTime)
9+import Data.Either (Either(..))
10+import Data.Enum (toEnum)
11+import Data.Maybe (Maybe(..), isNothing, maybe)
12+import Data.Time.Duration (Milliseconds(..))
13+import Data.Traversable (traverse)
14+import Data.Tuple (Tuple(..), uncurry)
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
20+import Database.IndexedDB.IDBKey (Key, none, toKey)
21+import Database.IndexedDB.IDBKeyRange as IDBKeyRange
22+import Database.IndexedDB.IDBObjectStore as IDBObjectStore
23+import Database.IndexedDB.IDBTransaction as IDBTransaction
24+import Effect (Effect)
25+import Effect.Aff (Aff, attempt, delay, forkAff, launchAff, launchAff_)
26+import Effect.Aff.AVar (AVar)
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)
3636
3737infixr 7 Tuple as :+:
3838
39−launchAff' :: forall a e. Aff e a -> Eff (exception :: EXCEPTION | e) Unit
40−launchAff' aff =
41− pure unit <* (launchAff aff)
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
4247
43−-- main :: forall eff. Eff (now :: NOW, mocha :: MOCHA, idb :: IDB, exception :: EXCEPTION, avar :: AVAR | eff) Unit
44−main = runMocha do
45− describe "IDBFactory" do
46− let
48+main :: Effect Unit
49+main = do
50+ launchAff_ $ runSpec [ consoleReporter ] $ do
51+ describe "IDBFactory" do
52+ let
4753 tearDown name version db = do
4854 IDBDatabase.close db
4955 version' <- IDBFactory.deleteDatabase name
56+ liftEffect $ log $ show version'
5057 version' `shouldEqual` version
5158
52− it "open default" do
53− let name = "db-default"
59+ it "open default" do
60+ let
61+ name = "db-default"
5462 version = 1
55− db <- IDBFactory.open name Nothing
56− { onUpgradeNeeded : Nothing
57− , onBlocked : Nothing
58− }
59− IDBDatabase.name db `shouldEqual` name
60− tearDown name version db
61−
63+ db <- IDBFactory.open name Nothing
64+ { onUpgradeNeeded: Nothing
65+ , onBlocked: Nothing
66+ }
67+ IDBDatabase.name db `shouldEqual` name
68+ tearDown name version db
6269
63− it "open specific version" do
64− let name = "db-specific"
70+ it "open specific version" do
71+ let
72+ name = "db-specific"
6573 version = 14
66− db <- IDBFactory.open name (Just version)
67− { onUpgradeNeeded : Nothing
68− , onBlocked : Nothing
69− }
70− IDBDatabase.name db `shouldEqual` name
71− tearDown name version db
72−
74+ db <- IDBFactory.open name (Just version)
75+ { onUpgradeNeeded: Nothing
76+ , onBlocked: Nothing
77+ }
78+ IDBDatabase.name db `shouldEqual` name
79+ tearDown name version db
7380
74− it "open specific version -> close -> open latest" do
75− let name = "db-latest"
81+ it "open specific version -> close -> open latest" do
82+ let
83+ name = "db-latest"
7684 version = 14
77− db <- IDBFactory.open name (Just version)
78− { onUpgradeNeeded : Nothing
79− , onBlocked : Nothing
80− }
81− IDBDatabase.name db `shouldEqual` name
82− IDBDatabase.close db
83− db <- IDBFactory.open name Nothing
84− { onUpgradeNeeded : Nothing
85− , onBlocked : Nothing
86− }
87− tearDown name version db
88−
89−
90− it "open + onUpgradeNeed" do
91− let name = "db-upgrade-needed"
85+ db01 <- IDBFactory.open name (Just version)
86+ { onUpgradeNeeded: Nothing
87+ , onBlocked: Nothing
88+ }
89+ IDBDatabase.name db01 `shouldEqual` name
90+ IDBDatabase.close db01
91+ db02 <- IDBFactory.open name Nothing
92+ { onUpgradeNeeded: Nothing
93+ , onBlocked: Nothing
94+ }
95+ tearDown name version db02
96+
97+ it "open + onUpgradeNeed" do
98+ let
99+ name = "db-upgrade-needed"
92100 version = 1
93− callback var db _ = do
94− _ <- launchAff $ modifyVar (const $ IDBDatabase.name db) var
101+ callback (Tuple varName varVersion) db _ { oldVersion } = do
102+ _ <- launchAff $ modifyAVar (const $ IDBDatabase.name db) varName
103+ _ <- launchAff $ modifyAVar (const $ oldVersion) varVersion
95104 pure unit
96− var <- makeVar' "-"
97− db <- IDBFactory.open name Nothing
98− { onUpgradeNeeded : Just (callback var)
99− , onBlocked : Nothing
100− }
101− name <- peekVar var
102− name `shouldEqual` name
103− tearDown name version db
104−
105−
106− it "open + onBlocked" do
107− let name = "db-blocked"
105+ varName <- AVar.new "-"
106+ varVersion <- AVar.new (-1)
107+ db <- IDBFactory.open name Nothing
108+ { onUpgradeNeeded: Just (callback (Tuple varName varVersion))
109+ , onBlocked: Nothing
110+ }
111+ name' <- AVar.read varName
112+ version' <- AVar.read varVersion
113+ name' `shouldEqual` name
114+ version' `shouldEqual` 0
115+ tearDown name version db
116+
117+ it "open + onBlocked" do
118+ let
119+ name = "db-blocked"
108120 version = 14
109121 callback var = do
110− _ <- launchAff $ modifyVar (const $ "db-blocked") var
122+ _ <- launchAff $ modifyAVar (const $ "db-blocked") var
111123 pure unit
112124
113− var <- makeVar' "-"
114− db01 <- IDBFactory.open name Nothing
115− { onUpgradeNeeded : Nothing
116− , onBlocked : Nothing
117− }
118− _ <- forkAff do
119− delay (Milliseconds 100.0)
120− IDBDatabase.close db01
125+ var <- AVar.new "-"
126+ db01 <- IDBFactory.open name Nothing
127+ { onUpgradeNeeded: Nothing
128+ , onBlocked: Nothing
129+ }
130+ _ <- forkAff do
131+ delay (Milliseconds 100.0)
132+ IDBDatabase.close db01
121133
122− db02 <- IDBFactory.open name (Just version)
123− { onUpgradeNeeded : Nothing
124− , onBlocked : Just (callback var)
125− }
126− name <- peekVar var
127− name `shouldEqual` name
128− tearDown name version db02
129−
130− describe "IDBKeyRange" do
131− it "only(int)" do
132− let key = 14
134+ db02 <- IDBFactory.open name (Just version)
135+ { onUpgradeNeeded: Nothing
136+ , onBlocked: Just (callback var)
137+ }
138+ name' <- AVar.read var
139+ name' `shouldEqual` name
140+ tearDown name version db02
141+
142+ describe "IDBKeyRange" do
143+ it "only(int)" do
144+ let
145+ key = 14
133146 range = IDBKeyRange.only key
134− IDBKeyRange.includes range (toKey key) `shouldEqual` true
147+ IDBKeyRange.includes range (toKey key) `shouldEqual` true
135148
136− it "only(string)" do
137− let key = "patate"
149+ it "only(string)" do
150+ let
151+ key = "patate"
138152 range = IDBKeyRange.only key
139− IDBKeyRange.includes range (toKey key) `shouldEqual` true
153+ IDBKeyRange.includes range (toKey key) `shouldEqual` true
140154
141− it "only(float)" do
142− let key = 14.42
155+ it "only(float)" do
156+ let
157+ key = 14.42
143158 range = IDBKeyRange.only key
144− IDBKeyRange.includes range (toKey key) `shouldEqual` true
159+ IDBKeyRange.includes range (toKey key) `shouldEqual` true
145160
146− it "only(date)" do
147− let mkey = DateTime
161+ it "only(date)" do
162+ let
163+ mkey = DateTime
148164 <$> (Date.canonicalDate <$> toEnum 2017 <*> toEnum 6 <*> toEnum 23)
149165 <*> (Time <$> toEnum 17 <*> toEnum 59 <*> toEnum 34 <*> toEnum 42)
150166
151− case mkey of
152− Nothing ->
153− fail "unable to create datetime"
154− Just key -> do
155− let range = IDBKeyRange.only key
156− IDBKeyRange.includes range (toKey key) `shouldEqual` true
167+ case mkey of
168+ Nothing ->
169+ fail "unable to create datetime"
170+ Just key -> do
171+ let range = IDBKeyRange.only key
172+ IDBKeyRange.includes range (toKey key) `shouldEqual` true
157173
158− it "only([int])" do
159− let key = [14, 42]
174+ it "only([int])" do
175+ let
176+ key = [ 14, 42 ]
160177 range = IDBKeyRange.only key
161− IDBKeyRange.includes range (toKey key) `shouldEqual` true
178+ IDBKeyRange.includes range (toKey key) `shouldEqual` true
162179
163− it "only([string])" do
164− let key = ["patate", "autruche"]
180+ it "only([string])" do
181+ let
182+ key = [ "patate", "autruche" ]
165183 range = IDBKeyRange.only key
166− IDBKeyRange.includes range (toKey key) `shouldEqual` true
184+ IDBKeyRange.includes range (toKey key) `shouldEqual` true
167185
168− it "lowerBound(14, open)" do
169− let key = 14
170− open = true
186+ it "lowerBound(14, open)" do
187+ let
188+ key = 14
189+ open = true
171190 range = IDBKeyRange.lowerBound key open
172− IDBKeyRange.includes range (toKey (key + 1)) `shouldEqual` true
173− IDBKeyRange.includes range (toKey key) `shouldEqual` (not open)
174− IDBKeyRange.includes range (toKey (key - 1)) `shouldEqual` false
175−
176− it "lowerBound(14, close)" do
177− let key = 14
178− open = false
191+ IDBKeyRange.includes range (toKey (key + 1)) `shouldEqual` true
192+ IDBKeyRange.includes range (toKey key) `shouldEqual` (not open)
193+ IDBKeyRange.includes range (toKey (key - 1)) `shouldEqual` false
194+
195+ it "lowerBound(14, close)" do
196+ let
197+ key = 14
198+ open = false
179199 range = IDBKeyRange.lowerBound key open
180− IDBKeyRange.includes range (toKey (key + 1)) `shouldEqual` true
181− IDBKeyRange.includes range (toKey key) `shouldEqual` (not open)
182− IDBKeyRange.includes range (toKey (key - 1)) `shouldEqual` false
183−
184− it "upperBound(14, open)" do
185− let key = 14
186− open = true
200+ IDBKeyRange.includes range (toKey (key + 1)) `shouldEqual` true
201+ IDBKeyRange.includes range (toKey key) `shouldEqual` (not open)
202+ IDBKeyRange.includes range (toKey (key - 1)) `shouldEqual` false
203+
204+ it "upperBound(14, open)" do
205+ let
206+ key = 14
207+ open = true
187208 range = IDBKeyRange.upperBound key open
188− IDBKeyRange.includes range (toKey (key + 1)) `shouldEqual` false
189− IDBKeyRange.includes range (toKey key) `shouldEqual` (not open)
190− IDBKeyRange.includes range (toKey (key - 1)) `shouldEqual` true
191−
192− it "upperBound(14, close)" do
193− let key = 14
194− open = false
209+ IDBKeyRange.includes range (toKey (key + 1)) `shouldEqual` false
210+ IDBKeyRange.includes range (toKey key) `shouldEqual` (not open)
211+ IDBKeyRange.includes range (toKey (key - 1)) `shouldEqual` true
212+
213+ it "upperBound(14, close)" do
214+ let
215+ key = 14
216+ open = false
195217 range = IDBKeyRange.upperBound key open
196− IDBKeyRange.includes range (toKey (key + 1)) `shouldEqual` false
197− IDBKeyRange.includes range (toKey key) `shouldEqual` (not open)
198− IDBKeyRange.includes range (toKey (key - 1)) `shouldEqual` true
199−
200− it "bound(42, 14, open, open) => Nothing" do
201− let lower = 42
202− upper = 14
218+ IDBKeyRange.includes range (toKey (key + 1)) `shouldEqual` false
219+ IDBKeyRange.includes range (toKey key) `shouldEqual` (not open)
220+ IDBKeyRange.includes range (toKey (key - 1)) `shouldEqual` true
221+
222+ it "bound(42, 14, open, open) => Nothing" do
223+ let
224+ lower = 42
225+ upper = 14
203226 lowerOpen = true
204227 upperOpen = true
205− mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
206− isNothing mrange `shouldEqual` true
228+ mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
229+ isNothing mrange `shouldEqual` true
207230
208− it "bound(14, 42, open, open)" do
209− let lower = 14
210− upper = 42
231+ it "bound(14, 42, open, open)" do
232+ let
233+ lower = 14
234+ upper = 42
211235 lowerOpen = true
212236 upperOpen = true
213− mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
214− case mrange of
215− Nothing ->
216− fail "invalid range provided"
217− Just range -> do
218− IDBKeyRange.includes range (toKey (lower + 1)) `shouldEqual` true
219− IDBKeyRange.includes range (toKey (upper - 1)) `shouldEqual` true
220− IDBKeyRange.includes range (toKey (lower - 1)) `shouldEqual` false
221− IDBKeyRange.includes range (toKey (upper + 1)) `shouldEqual` false
222− IDBKeyRange.includes range (toKey lower) `shouldEqual` (not lowerOpen)
223− IDBKeyRange.includes range (toKey upper) `shouldEqual` (not upperOpen)
224−
225− it "bound(14, 42, open, close)" do
226− let lower = 14
227− upper = 42
237+ mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
238+ case mrange of
239+ Nothing ->
240+ fail "invalid range provided"
241+ Just range -> do
242+ IDBKeyRange.includes range (toKey (lower + 1)) `shouldEqual` true
243+ IDBKeyRange.includes range (toKey (upper - 1)) `shouldEqual` true
244+ IDBKeyRange.includes range (toKey (lower - 1)) `shouldEqual` false
245+ IDBKeyRange.includes range (toKey (upper + 1)) `shouldEqual` false
246+ IDBKeyRange.includes range (toKey lower) `shouldEqual` (not lowerOpen)
247+ IDBKeyRange.includes range (toKey upper) `shouldEqual` (not upperOpen)
248+
249+ it "bound(14, 42, open, close)" do
250+ let
251+ lower = 14
252+ upper = 42
228253 lowerOpen = true
229254 upperOpen = false
230− mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
231− case mrange of
232− Nothing ->
233− fail "invalid range provided"
234− Just range -> do
235− IDBKeyRange.includes range (toKey (lower + 1)) `shouldEqual` true
236− IDBKeyRange.includes range (toKey (upper - 1)) `shouldEqual` true
237− IDBKeyRange.includes range (toKey (lower - 1)) `shouldEqual` false
238− IDBKeyRange.includes range (toKey (upper + 1)) `shouldEqual` false
239− IDBKeyRange.includes range (toKey lower) `shouldEqual` (not lowerOpen)
240− IDBKeyRange.includes range (toKey upper) `shouldEqual` (not upperOpen)
241−
242− it "bound(14, 42, close, open)" do
243− let lower = 14
244− upper = 42
255+ mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
256+ case mrange of
257+ Nothing ->
258+ fail "invalid range provided"
259+ Just range -> do
260+ IDBKeyRange.includes range (toKey (lower + 1)) `shouldEqual` true
261+ IDBKeyRange.includes range (toKey (upper - 1)) `shouldEqual` true
262+ IDBKeyRange.includes range (toKey (lower - 1)) `shouldEqual` false
263+ IDBKeyRange.includes range (toKey (upper + 1)) `shouldEqual` false
264+ IDBKeyRange.includes range (toKey lower) `shouldEqual` (not lowerOpen)
265+ IDBKeyRange.includes range (toKey upper) `shouldEqual` (not upperOpen)
266+
267+ it "bound(14, 42, close, open)" do
268+ let
269+ lower = 14
270+ upper = 42
245271 lowerOpen = false
246272 upperOpen = true
247− mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
248− case mrange of
249− Nothing ->
250− fail "invalid range provided"
251− Just range -> do
252− IDBKeyRange.includes range (toKey (lower + 1)) `shouldEqual` true
253− IDBKeyRange.includes range (toKey (upper - 1)) `shouldEqual` true
254− IDBKeyRange.includes range (toKey (lower - 1)) `shouldEqual` false
255− IDBKeyRange.includes range (toKey (upper + 1)) `shouldEqual` false
256− IDBKeyRange.includes range (toKey lower) `shouldEqual` (not lowerOpen)
257− IDBKeyRange.includes range (toKey upper) `shouldEqual` (not upperOpen)
258−
259− it "bound(14, 42, close, close)" do
260− let lower = 14
261− upper = 42
273+ mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
274+ case mrange of
275+ Nothing ->
276+ fail "invalid range provided"
277+ Just range -> do
278+ IDBKeyRange.includes range (toKey (lower + 1)) `shouldEqual` true
279+ IDBKeyRange.includes range (toKey (upper - 1)) `shouldEqual` true
280+ IDBKeyRange.includes range (toKey (lower - 1)) `shouldEqual` false
281+ IDBKeyRange.includes range (toKey (upper + 1)) `shouldEqual` false
282+ IDBKeyRange.includes range (toKey lower) `shouldEqual` (not lowerOpen)
283+ IDBKeyRange.includes range (toKey upper) `shouldEqual` (not upperOpen)
284+
285+ it "bound(14, 42, close, close)" do
286+ let
287+ lower = 14
288+ upper = 42
262289 lowerOpen = false
263290 upperOpen = false
264− mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
265− case mrange of
266− Nothing ->
267− fail "invalid range provided"
268− Just range -> do
269− IDBKeyRange.includes range (toKey (lower + 1)) `shouldEqual` true
270− IDBKeyRange.includes range (toKey (upper - 1)) `shouldEqual` true
271− IDBKeyRange.includes range (toKey (lower - 1)) `shouldEqual` false
272− IDBKeyRange.includes range (toKey (upper + 1)) `shouldEqual` false
273− IDBKeyRange.includes range (toKey lower) `shouldEqual` (not lowerOpen)
274− IDBKeyRange.includes range (toKey upper) `shouldEqual` (not upperOpen)
275−
276− it "can access attributes of a range" do
277− let range = IDBKeyRange.lowerBound 14 false
278− IDBKeyRange.lower range `shouldEqual` (Just $ toKey 14)
279− IDBKeyRange.upper range `shouldEqual` (Nothing :: Maybe Key)
280− IDBKeyRange.lowerOpen range `shouldEqual` false
281− IDBKeyRange.upperOpen range `shouldEqual` true
282−
283− describe "IDBDatabase" do
284− let
291+ mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
292+ case mrange of
293+ Nothing ->
294+ fail "invalid range provided"
295+ Just range -> do
296+ IDBKeyRange.includes range (toKey (lower + 1)) `shouldEqual` true
297+ IDBKeyRange.includes range (toKey (upper - 1)) `shouldEqual` true
298+ IDBKeyRange.includes range (toKey (lower - 1)) `shouldEqual` false
299+ IDBKeyRange.includes range (toKey (upper + 1)) `shouldEqual` false
300+ IDBKeyRange.includes range (toKey lower) `shouldEqual` (not lowerOpen)
301+ IDBKeyRange.includes range (toKey upper) `shouldEqual` (not upperOpen)
302+
303+ it "can access attributes of a range" do
304+ let range = IDBKeyRange.lowerBound 14 false
305+ IDBKeyRange.lower range `shouldEqual` (Just $ toKey 14)
306+ IDBKeyRange.upper range `shouldEqual` none
307+ IDBKeyRange.lowerOpen range `shouldEqual` false
308+ IDBKeyRange.upperOpen range `shouldEqual` true
309+
310+ describe "IDBDatabase" do
311+ let
285312 tearDown db = do
286313 IDBDatabase.close db
287314 _ <- IDBFactory.deleteDatabase (IDBDatabase.name db)
288315 pure unit
289316
290317 setup storeParams = do
291− let onUpgradeNeeded var db _ = launchAff' do
292− store <- IDBDatabase.createObjectStore db "store" storeParams
293− _ <- putVar var { db, store }
294− pure unit
295−
296− var <- makeVar
297− db <- IDBFactory.open "db" Nothing
298− { onUpgradeNeeded : Just (onUpgradeNeeded var)
299− , onBlocked : Nothing
318+ let
319+ onUpgradeNeeded var db _ _ = launchAff' do
320+ store <- IDBDatabase.createObjectStore db "store" storeParams
321+ _ <- AVar.put { db, store } var
322+ pure unit
323+
324+ var <- AVar.empty
325+ _ <- IDBFactory.open "db" Nothing
326+ { onUpgradeNeeded: Just (onUpgradeNeeded var)
327+ , onBlocked: Nothing
300328 }
301329
302− takeVar var
303−
304− it "createObjectStore (keyPath: [], autoIncrement: true)" do
305− { db, store } <- setup { keyPath: [], autoIncrement: true }
306− IDBObjectStore.name store `shouldEqual` "store"
307− IDBObjectStore.keyPath store `shouldEqual` []
308− IDBObjectStore.autoIncrement store `shouldEqual` true
309− IDBObjectStore.indexNames store `shouldEqual` []
310− tearDown db
311−
312− it "createObjectStore (keyPath: [\"patate\"], autoIncrement: true)" do
313− { db, store } <- setup { keyPath: ["patate"], autoIncrement: true }
314− IDBObjectStore.name store `shouldEqual` "store"
315− IDBObjectStore.keyPath store `shouldEqual` ["patate"]
316− IDBObjectStore.autoIncrement store `shouldEqual` true
317− IDBObjectStore.indexNames store `shouldEqual` []
318− tearDown db
319−
320− it "createObjectStore (keyPath: [\"a\", \"b\"], autoIncrement: false)" do
321− { db, store } <- setup { keyPath: ["patate", "autruche"], autoIncrement: false }
322− IDBObjectStore.name store `shouldEqual` "store"
323− IDBObjectStore.keyPath store `shouldEqual` ["patate", "autruche"]
324− IDBObjectStore.autoIncrement store `shouldEqual` false
325− IDBObjectStore.indexNames store `shouldEqual` []
326− tearDown db
327−
328− it "deleteObjectStore" do
329− let onUpgradeNeeded var db _ = launchAff' do
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
330359 _ <- IDBDatabase.deleteObjectStore db "store"
331− putVar var true
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)
367+ , onBlocked: Nothing
368+ }
369+ deleted <- AVar.take var
370+ deleted `shouldEqual` true
371+ tearDown db'
332372
333− var <- makeVar
334− { db, store } <- setup IDBObjectStore.defaultParameters
335− IDBDatabase.close db
336− db <- IDBFactory.open "db" (Just 999) { onUpgradeNeeded : Just (onUpgradeNeeded var)
337− , onBlocked : Nothing
338− }
339− deleted <- takeVar var
340− deleted `shouldEqual` true
341− tearDown db
373+ describe "IDBObjectStore" do
374+ let
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
388+ _ <- IDBFactory.open "db" Nothing
389+ { onUpgradeNeeded: Just (onUpgradeNeeded' var)
390+ , onBlocked: Nothing
391+ }
392+
393+ AVar.take var
394+
395+ it "add()" do
396+ date <- liftEffect $ toDateTime <$> now
397+ { db } <- setup
398+ { storeParams: { autoIncrement: true, keyPath: [] }
399+ , onUpgradeNeeded: Just $ \_ store -> launchAff' do
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''
342411
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
429+ , onUpgradeNeeded: Just $ \_ store -> launchAff' do
430+ key <- IDBObjectStore.add store "patate" (Just 14)
431+ _ <- IDBObjectStore.clear store
432+ val <- IDBObjectStore.get store (IDBKeyRange.only key)
433+ val `shouldEqual` (Nothing :: Maybe String)
434+ }
435+ tearDown db
436+
437+ it "count()" do
438+ { db } <- setup
439+ { storeParams: IDBDatabase.defaultParameters
440+ , onUpgradeNeeded: Just $ \_ store -> launchAff' do
441+ _ <- IDBObjectStore.add store "patate" (Just 14)
442+ _ <- IDBObjectStore.add store "autruche" (Just 42)
443+ n <- IDBObjectStore.count store Nothing
444+ n `shouldEqual` 2
445+ }
446+ tearDown db
447+
448+ it "getKey()" do
449+ { db } <- setup
450+ { storeParams: IDBDatabase.defaultParameters
451+ , onUpgradeNeeded: Just $ \_ store -> launchAff' do
452+ key <- IDBObjectStore.add store "patate" (Just 14)
453+ mkey <- IDBObjectStore.getKey store (IDBKeyRange.only 14)
454+ mkey `shouldEqual` (Just key)
455+
456+ mkey' <- IDBObjectStore.getKey store (IDBKeyRange.only 42)
457+ mkey' `shouldEqual` none
458+ }
459+ tearDown db
460+
461+ it "getAllKeys()" do
462+ { db } <- setup
463+ { storeParams: IDBDatabase.defaultParameters
464+ , onUpgradeNeeded: Just $ \_ store -> launchAff' do
465+ key1 <- IDBObjectStore.add store "patate" (Just 14)
466+ key2 <- IDBObjectStore.add store "autruche" (Just 42)
467+ key3 <- IDBObjectStore.add store 14 (Just 1337)
468+
469+ -- no bounds
470+ keys <- IDBObjectStore.getAllKeys store Nothing Nothing
471+ keys `shouldEqual` [ key1, key2, key3 ]
472+
473+ -- lower bound
474+ keys' <- IDBObjectStore.getAllKeys store (Just $ IDBKeyRange.lowerBound 14 true) Nothing
475+ keys' `shouldEqual` [ key2, key3 ]
476+
477+ -- upper bound
478+ keys'' <- IDBObjectStore.getAllKeys store (Just $ IDBKeyRange.upperBound 42 false) Nothing
479+ keys'' `shouldEqual` [ key1, key2 ]
480+
481+ -- count
482+ keys''' <- IDBObjectStore.getAllKeys store (Just $ IDBKeyRange.lowerBound 1 true) (Just 2)
483+ keys''' `shouldEqual` [ key1, key2 ]
484+ }
485+ tearDown db
486+
487+ it "openCursor()" do
488+ let
489+ cb =
490+ { onSuccess: const $ pure unit
491+ , onError: show >>> fail >>> launchAff'
492+ , onComplete: pure unit
493+ }
494+ { db } <- setup
495+ { storeParams: IDBDatabase.defaultParameters
496+ , onUpgradeNeeded: Just $ \_ store -> launchAff' do
497+ _ <- IDBObjectStore.openCursor store Nothing Next cb
498+ _ <- IDBObjectStore.openCursor store Nothing NextUnique cb
499+ _ <- IDBObjectStore.openCursor store Nothing Prev cb
500+ _ <- IDBObjectStore.openCursor store Nothing PrevUnique cb
501+ _ <- IDBObjectStore.openCursor store (Just $ IDBKeyRange.upperBound 1 true) Next cb
502+ pure unit
503+ }
504+ tearDown db
505+
506+ it "openKeyCursor()" do
507+ let
508+ cb =
509+ { onSuccess: const $ pure unit
510+ , onError: show >>> fail >>> launchAff'
511+ , onComplete: pure unit
512+ }
513+ { db } <- setup
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
520+ _ <- IDBObjectStore.openKeyCursor store (Just $ IDBKeyRange.lowerBound 1 true) Next cb
521+ pure unit
522+ }
523+ tearDown db
343524
344− describe "IDBObjectStore" do
345− let
525+ describe "IDBIndex" do
526+ let
346527 tearDown db = do
347528 IDBDatabase.close db
348529 _ <- IDBFactory.deleteDatabase (IDBDatabase.name db)
349530 pure unit
350531
351− setup { storeParams, onUpgradeNeeded } = do
352− let onUpgradeNeeded' var db _ = launchAff' do
353− store <- IDBDatabase.createObjectStore db "store" storeParams
354− liftEff $ maybe (pure unit) id (onUpgradeNeeded <*> pure db <*> pure store)
355− putVar var { db, store }
356−
357− var <- makeVar
358− db <- IDBFactory.open "db" Nothing
359− { onUpgradeNeeded : Just (onUpgradeNeeded' var)
360− , onBlocked : Nothing
532+ setup
533+ :: forall value
534+ . { storeParams :: { keyPath :: Array String, autoIncrement :: Boolean }
535+ , indexParams :: { unique :: Boolean, multiEntry :: Boolean }
536+ , values :: Array (Tuple value (Maybe Key))
537+ , keyPath :: Array String
538+ , onUpgradeNeeded :: Maybe (Database -> Transaction -> Index -> Effect Unit)
539+ }
540+ -> Aff { db :: Database, index :: Index, store :: ObjectStore }
541+ setup { storeParams, indexParams, values, keyPath, onUpgradeNeeded } = do
542+ let
543+ onUpgradeNeeded' var db tx _ = launchAff' do
544+ store <- IDBDatabase.createObjectStore db "store" storeParams
545+ _ <- traverse (uncurry (IDBObjectStore.add store)) values
546+ index <- IDBObjectStore.createIndex store "index" keyPath indexParams
547+ liftEffect $ maybe (pure unit) identity (onUpgradeNeeded <*> pure db <*> pure tx <*> pure index)
548+ AVar.put { db, index, store } var
549+
550+ var <- AVar.empty
551+ _ <- IDBFactory.open "db" Nothing
552+ { onUpgradeNeeded: Just (onUpgradeNeeded' var)
553+ , onBlocked: Nothing
554+ }
555+ AVar.take var
556+
557+ it "returns an IDBIndex and the properties are set correctly" do
558+ { db, index } <- setup
559+ { storeParams: IDBDatabase.defaultParameters
560+ , indexParams: IDBObjectStore.defaultParameters
561+ , onUpgradeNeeded: Nothing
562+ , keyPath: []
563+ , values: []
564+ }
565+ IDBIndex.name index `shouldEqual` "index"
566+ IDBIndex.keyPath index `shouldEqual` []
567+ IDBIndex.unique index `shouldEqual` IDBObjectStore.defaultParameters.unique
568+ IDBIndex.multiEntry index `shouldEqual` IDBObjectStore.defaultParameters.multiEntry
569+ tearDown db
570+
571+ it "attempt to create an index that requires unique values on an object store already contains duplicates" do
572+ let onAbort var = launchAff' (AVar.put true var)
573+ txVar <- AVar.empty
574+ dbVar <- AVar.empty
575+ res <- attempt $ setup
576+ { storeParams: IDBDatabase.defaultParameters
577+ , indexParams:
578+ { unique: true
579+ , multiEntry: false
580+ }
581+ , keyPath: [ "indexedProperty" ]
582+ , values:
583+ [ { indexedProperty: "bar" } :+: (Just $ toKey 1)
584+ , { indexedProperty: "bar" } :+: (Just $ toKey 2)
585+ ]
586+ , onUpgradeNeeded: Just $ \db tx _ -> launchAff' do
587+ IDBTransaction.onAbort tx (onAbort txVar)
588+ IDBDatabase.onAbort db (onAbort dbVar)
589+ }
590+ case res of
591+ Right _ ->
592+ fail "expected abort"
593+ _ -> do
594+ shouldEqual true =<< AVar.take txVar
595+ shouldEqual true =<< AVar.take dbVar
596+
597+ it "the index is usable right after being made" do
598+ { db } <- setup
599+ { storeParams:
600+ { keyPath: [ "key" ]
601+ , autoIncrement: false
602+ }
603+ , indexParams: IDBObjectStore.defaultParameters
604+ , keyPath: [ "indexedProperty" ]
605+ , values:
606+ [ { key: "key1", indexedProperty: "indexed_1" } :+: Nothing
607+ , { key: "key2", indexedProperty: "indexed_2" } :+: Nothing
608+ , { key: "key3", indexedProperty: "indexed_3" } :+: Nothing
609+ ]
610+ , onUpgradeNeeded: Just $ \_ _ index -> launchAff' do
611+ val <- IDBIndex.get index (IDBKeyRange.only "indexed_2")
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
361728 }
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+ }
362763
363− takeVar var
364−
365− it "add()" do
366− date <- liftEff $ toDateTime <$> now
367− { db } <- setup
368− { storeParams: { autoIncrement: true, keyPath: [] }
369− , onUpgradeNeeded: Just $ \_ store -> launchAff' do
370− -- no key
371− key <- IDBObjectStore.add store "patate" (Nothing :: Maybe Key)
372− (toKey 1) `shouldEqual` key
373−
374− -- int key
375− key <- IDBObjectStore.add store "patate" (Just 14)
376− (toKey 14) `shouldEqual` key
377−
378− -- number key
379− key <- IDBObjectStore.add store "patate" (Just 14.42)
380− (toKey 14.42) `shouldEqual` key
381−
382− -- string key
383− key <- IDBObjectStore.add store "patate" (Just "key")
384− (toKey "key") `shouldEqual` key
385−
386− -- date key
387− key <- IDBObjectStore.add store "patate" (Just date)
388− (toKey date) `shouldEqual` key
389−
390− -- array key
391− key <- IDBObjectStore.add store "patate" (Just $ toKey [14, 42])
392− (toKey [14, 42]) `shouldEqual` key
393− }
394− tearDown db
395−
396− it "clear()" do
397− { db } <- setup
398− { storeParams: IDBObjectStore.defaultParameters
399− , onUpgradeNeeded: Just $ \_ store -> launchAff' do
400− key <- IDBObjectStore.add store "patate" (Just 14)
401− _ <- IDBObjectStore.clear store
402− val <- IDBObjectStore.get store (IDBKeyRange.only key)
403− val `shouldEqual` (Nothing :: Maybe String)
404− }
405− tearDown db
406−
407− it "count()" do
408− { db } <- setup
409− { storeParams: IDBObjectStore.defaultParameters
410− , onUpgradeNeeded: Just $ \_ store -> launchAff' do
411− key <- IDBObjectStore.add store "patate" (Just 14)
412− key <- IDBObjectStore.add store "autruche" (Just 42)
413− n <- IDBObjectStore.count store Nothing
414− n `shouldEqual` 2
415− }
416− tearDown db
417−
418− it "getKey()" do
419− { db } <- setup
420− { storeParams: IDBObjectStore.defaultParameters
421− , onUpgradeNeeded: Just $ \_ store -> launchAff' do
422− key <- IDBObjectStore.add store "patate" (Just 14)
423− mkey <- IDBObjectStore.getKey store (IDBKeyRange.only 14)
424− mkey `shouldEqual` (Just key)
425−
426− mkey <- IDBObjectStore.getKey store (IDBKeyRange.only 42)
427− mkey `shouldEqual` (Nothing :: Maybe Key)
428− }
429− tearDown db
430−
431− it "getAllKeys()" do
432− { db } <- setup
433− { storeParams: IDBObjectStore.defaultParameters
434− , onUpgradeNeeded: Just $ \_ store -> launchAff' do
435− key1 <- IDBObjectStore.add store "patate" (Just 14)
436− key2 <- IDBObjectStore.add store "autruche" (Just 42)
437− key3 <- IDBObjectStore.add store 14 (Just 1337)
438−
439− -- no bounds
440− keys <- IDBObjectStore.getAllKeys store Nothing Nothing
441− keys `shouldEqual` [key1, key2, key3]
442−
443− -- lower bound
444− keys <- IDBObjectStore.getAllKeys store (Just $ IDBKeyRange.lowerBound 14 true) Nothing
445− keys `shouldEqual` [key2, key3]
446−
447− -- upper bound
448− keys <- IDBObjectStore.getAllKeys store (Just $ IDBKeyRange.upperBound 42 false) Nothing
449− keys `shouldEqual` [key1, key2]
450−
451− -- count
452− keys <- IDBObjectStore.getAllKeys store (Just $ IDBKeyRange.lowerBound 1 true) (Just 2)
453− keys `shouldEqual` [key1, key2]
454− }
455− tearDown db
456−
457− it "openCursor()" do
458− { db } <- setup
459− { storeParams: IDBObjectStore.defaultParameters
460− , onUpgradeNeeded: Just $ \_ store -> launchAff' do
461− _ <- IDBObjectStore.openCursor store Nothing Next
462− _ <- IDBObjectStore.openCursor store Nothing NextUnique
463− _ <- IDBObjectStore.openCursor store Nothing Prev
464− _ <- IDBObjectStore.openCursor store Nothing PrevUnique
465− _ <- IDBObjectStore.openCursor store (Just $ IDBKeyRange.upperBound 1 true) Next
466− pure unit
467− }
468− tearDown db
469−
470− it "openKeyCursor()" do
471− { db } <- setup
472− { storeParams: IDBObjectStore.defaultParameters
473− , onUpgradeNeeded: Just $ \_ store -> launchAff' do
474− _ <- IDBObjectStore.openKeyCursor store Nothing Next
475− _ <- IDBObjectStore.openKeyCursor store Nothing NextUnique
476− _ <- IDBObjectStore.openKeyCursor store Nothing Prev
477− _ <- IDBObjectStore.openKeyCursor store Nothing PrevUnique
478− _ <- IDBObjectStore.openKeyCursor store (Just $ IDBKeyRange.lowerBound 1 true) Next
479− pure unit
480− }
481− tearDown db
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)
482885
483− describe "IDBIndex" do
484− let
886+ tearDown db
887+
888+ describe "IDBCursor" do
889+ let
485890 tearDown db = do
486891 IDBDatabase.close db
487892 _ <- IDBFactory.deleteDatabase (IDBDatabase.name db)
488893 pure unit
489894
490− setup :: forall value e e'.
491− { storeParams :: { keyPath :: Array String, autoIncrement :: Boolean }
492− , indexParams :: { unique :: Boolean, multiEntry :: Boolean }
493− , values :: Array (Tuple value (Maybe Key))
494− , keyPath :: Array String
495− , onUpgradeNeeded :: Maybe (Database -> Transaction -> Index -> Eff (idb :: IDB, avar :: AVAR, exception :: EXCEPTION | e') Unit)
496− } -> Aff (idb :: IDB, avar :: AVAR | e) { db :: Database, index :: Index, store :: ObjectStore }
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 }
497904 setup { storeParams, indexParams, values, keyPath, onUpgradeNeeded } = do
498− let onUpgradeNeeded' var db tx = launchAff' do
499− store <- IDBDatabase.createObjectStore db "store" storeParams
500− _ <- traverse (uncurry (IDBObjectStore.add store)) values
501− index <- IDBObjectStore.createIndex store "index" keyPath indexParams
502− liftEff $ maybe (pure unit) id (onUpgradeNeeded <*> pure db <*> pure tx <*> pure index)
503− putVar var { db, index, store }
504−
505− var <- makeVar
506− db <- IDBFactory.open "db" Nothing
507− { onUpgradeNeeded : Just (onUpgradeNeeded' var)
508− , onBlocked : Nothing
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"
509990 }
510− takeVar var
511−
512−
513− it "returns an IDBIndex and the properties are set correctly" do
514− { db, index } <- setup
515− { storeParams : IDBObjectStore.defaultParameters
516− , indexParams : IDBIndex.defaultParameters
517− , onUpgradeNeeded : Nothing
518− , keyPath : []
519− , values : []
520− }
521− IDBIndex.name index `shouldEqual` "index"
522− IDBIndex.keyPath index `shouldEqual` []
523− IDBIndex.unique index `shouldEqual` IDBIndex.defaultParameters.unique
524− IDBIndex.multiEntry index `shouldEqual` IDBIndex.defaultParameters.multiEntry
525− tearDown db
526−
527− it "attempt to create an index that requires unique values on an object store already contains duplicates" do
528− let onAbort var = launchAff' (putVar var true)
529− txVar <- makeVar
530− dbVar <- makeVar
531− res <- attempt $ setup
532− { storeParams : IDBObjectStore.defaultParameters
533− , indexParams : { unique : true
534− , multiEntry : false
535− }
536− , keyPath : ["indexedProperty"]
537− , values : [ { indexedProperty: "bar" } :+: (Just $ toKey 1)
538− , { indexedProperty: "bar" } :+: (Just $ toKey 2)
539− ]
540− , onUpgradeNeeded : Just $ \db tx _ -> launchAff' do
541− IDBTransaction.onAbort tx (onAbort txVar)
542− IDBDatabase.onAbort db (onAbort dbVar)
543− }
544− case res of
545− Right _ ->
546− fail "expected abort"
547− _ -> do
548− shouldEqual true =<< takeVar txVar
549− shouldEqual true =<< takeVar dbVar
550−
551−
552− it "the index is usable right after being made" do
553− { db } <- setup
554− { storeParams : { keyPath : ["key"]
555− , autoIncrement : false
556− }
557− , indexParams : IDBIndex.defaultParameters
558− , keyPath : ["indexedProperty"]
559− , values : [ { key: "key1", indexedProperty: "indexed_1" } :+: Nothing
560− , { key: "key2", indexedProperty: "indexed_2" } :+: Nothing
561− , { key: "key3", indexedProperty: "indexed_3" } :+: Nothing
562− ]
563− , onUpgradeNeeded : Just $ \_ _ index -> launchAff' do
564− val <- IDBIndex.get index (IDBKeyRange.only "indexed_2")
565− ((\r -> r.key) <$> val) `shouldEqual` (Just $ toKey "key2")
566− }
567− tearDown db
568−
569− it "empty keyPath" do
570− { db } <- setup
571− { storeParams : IDBObjectStore.defaultParameters
572− , indexParams : IDBIndex.defaultParameters
573− , keyPath : []
574− , values : [ "object_1" :+: (Just $ toKey 1)
575− , "object_2" :+: (Just $ toKey 2)
576− , "object_3" :+: (Just $ toKey 3)
577− ]
578− , onUpgradeNeeded : Just $ \_ _ index -> launchAff' do
579− val <- IDBIndex.get index (IDBKeyRange.only "object_3")
580− val `shouldEqual` (Just "object_3")
581− }
582− tearDown db
583−
584− it "index can be valid keys [date]" do
585− date <- liftEff $ toDateTime <$> now
586− { db } <- setup
587− { storeParams : { keyPath: ["key"]
588− , autoIncrement: false
589− }
590− , indexParams : IDBIndex.defaultParameters
591− , keyPath : ["i"]
592− , values : [ { key: "date", i: (toKey date) } :+: Nothing
593− ]
594− , onUpgradeNeeded : Just $ \_ _ index -> launchAff' do
595− val <- IDBIndex.get index (IDBKeyRange.only date)
596− ((\r -> r.key) <$> val) `shouldEqual` (Just "date")
597− }
598− tearDown db
599−
600− it "index can be valid keys [num]" do
601− let num = 14
602− { db } <- setup
603− { storeParams : { keyPath: ["key"]
604− , autoIncrement: false
605− }
606− , indexParams : IDBIndex.defaultParameters
607− , keyPath : ["i"]
608− , values : [ { key: "num", i: (toKey num) } :+: Nothing
609− ]
610− , onUpgradeNeeded : Just $ \_ _ index -> launchAff' do
611− val <- IDBIndex.get index (IDBKeyRange.only num)
612− ((\r -> r.key) <$> val) `shouldEqual` (Just "num")
613− }
614− tearDown db
615−
616− it "index can be valid keys [array]" do
617− let array = ["patate", "autruche"]
618− { db } <- setup
619− { storeParams : { keyPath: ["key"]
620− , autoIncrement: false
621− }
622− , indexParams : IDBIndex.defaultParameters
623− , keyPath : ["i"]
624− , values : [ { key: "array", i: (toKey array) } :+: Nothing
625− ]
626− , onUpgradeNeeded : Just $ \_ _ index -> launchAff' do
627− val <- IDBIndex.get index (IDBKeyRange.only array)
628− ((\r -> r.key) <$> val) `shouldEqual` (Just "array")
629− }
630− tearDown db
631−
632− it "openKeyCursor() - throw InvalidStateError on index deleted by aborted upgrade" do
633− res <- attempt $ setup
634− { storeParams : { keyPath: ["key"], autoIncrement: false }
635− , indexParams : IDBIndex.defaultParameters
636− , keyPath : ["indexedProperty"]
637− , values : [ { key: 14, indexedProperty: "patate" } :+: Nothing
638− ]
639− , onUpgradeNeeded : Just $ \db tx index -> launchAff' do
640− IDBTransaction.onAbort tx (pure unit)
641− IDBDatabase.onAbort db (pure unit)
642− IDBTransaction.abort tx
643− cursor <- attempt $ IDBIndex.openKeyCursor index Nothing Next
644− case cursor of
645− Right _ -> fail "expected InvalidStateError"
646− Left err -> message err `shouldEqual` "InvalidStateError"
647− }
648− case res of
649− Right _ -> fail "expected InvalidStateError"
650− _ -> pure unit
651−
652− it "openKeyCursor() - throw TransactionInactiveError on aborted transaction" do
653− { db } <- setup
654− { storeParams : { keyPath: ["key"], autoIncrement: false }
655− , indexParams : IDBIndex.defaultParameters
656− , keyPath : ["indexedProperty"]
657− , values : [ { key: 14, indexedProperty: "patate" } :+: Nothing
658− ]
659− , onUpgradeNeeded : Nothing
660− }
661−
662− tx <- IDBDatabase.transaction db ["store"] ReadOnly
663− store <- IDBTransaction.objectStore tx "store"
664− index <- IDBObjectStore.index store "index"
665− IDBTransaction.onAbort tx (pure unit)
666− IDBTransaction.abort tx
667− cursor <- attempt $ IDBIndex.openKeyCursor index Nothing Next
668− case cursor of
669− Right _ -> fail "expected TransactionInactiveError"
670− Left err -> message err `shouldEqual` "TransactionInactiveError"
671−
672− tearDown db
673−
674− it "openKeyCursor() - throw InvalidStateError when the index is deleted" do
675− { db } <- setup
676− { storeParams : { keyPath: ["key"], autoIncrement: false }
677− , indexParams : IDBIndex.defaultParameters
678− , keyPath : ["indexedProperty"]
679− , values : [ { key: 14, indexedProperty: "patate" } :+: Nothing
680− ]
681− , onUpgradeNeeded : Just $ \db tx index -> launchAff' do
682− store <- IDBTransaction.objectStore tx "store"
683− IDBObjectStore.deleteIndex store "index"
684− cursor <- attempt $ IDBIndex.openKeyCursor index Nothing Next
685− case cursor of
686− Right _ -> fail "expected InvalidStateError"
687− Left err -> message err `shouldEqual` "InvalidStateError"
688− }
689−
690− tearDown db
691−
692− it "openCursor() - throw InvalidStateError on index deleted by aborted upgrade" do
693− res <- attempt $ setup
694− { storeParams : { keyPath: ["key"], autoIncrement: false }
695− , indexParams : IDBIndex.defaultParameters
696− , keyPath : ["indexedProperty"]
697− , values : [ { key: 14, indexedProperty: "patate" } :+: Nothing
698− ]
699− , onUpgradeNeeded : Just $ \db tx index -> launchAff' do
700− IDBTransaction.onAbort tx (pure unit)
701− IDBDatabase.onAbort db (pure unit)
702− IDBTransaction.abort tx
703− cursor <- attempt $ IDBIndex.openCursor index Nothing Next
704− case cursor of
705− Right _ -> fail "expected InvalidStateError"
706− Left err -> message err `shouldEqual` "InvalidStateError"
707− }
708− case res of
709− Right _ -> fail "expected InvalidStateError"
710− _ -> pure unit
711−
712− it "openCursor() - throw TransactionInactiveError on aborted transaction" do
713− { db } <- setup
714− { storeParams : { keyPath: ["key"], autoIncrement: false }
715− , indexParams : IDBIndex.defaultParameters
716− , keyPath : ["indexedProperty"]
717− , values : [ { key: 14, indexedProperty: "patate" } :+: Nothing
718− ]
719− , onUpgradeNeeded : Nothing
720− }
721−
722− tx <- IDBDatabase.transaction db ["store"] ReadOnly
723− store <- IDBTransaction.objectStore tx "store"
724− index <- IDBObjectStore.index store "index"
725− IDBTransaction.onAbort tx (pure unit)
726− IDBTransaction.abort tx
727− cursor <- attempt $ IDBIndex.openCursor index Nothing Next
728− case cursor of
729− Right _ -> fail "expected TransactionInactiveError"
730− Left err -> message err `shouldEqual` "TransactionInactiveError"
731−
732− tearDown db
733−
734− it "openCursor() - throw InvalidStateError when the index is deleted" do
735− { db } <- setup
736− { storeParams : { keyPath: ["key"], autoIncrement: false }
737− , indexParams : IDBIndex.defaultParameters
738− , keyPath : ["indexedProperty"]
739− , values : [ { key: 14, indexedProperty: "patate" } :+: Nothing
740− ]
741− , onUpgradeNeeded : Just $ \db tx index -> launchAff' do
742− store <- IDBTransaction.objectStore tx "store"
743− IDBObjectStore.deleteIndex store "index"
744− cursor <- attempt $ IDBIndex.openCursor index Nothing Next
745− case cursor of
746− Right _ -> fail "expected InvalidStateError"
747− Left err -> message err `shouldEqual` "InvalidStateError"
748− }
749−
750− tearDown db
751−
752− it "getKey() - multiEntry - adding keys" do
753− { db } <- setup
754− { storeParams : IDBObjectStore.defaultParameters
755− , indexParams : { unique: false, multiEntry: true }
756− , keyPath : ["name"]
757− , values : [ { name: ["patate", "autruche"] } :+: (Just $ toKey 1)
758− , { name: ["bob"] } :+: (Just $ toKey 2)
759− ]
760− , onUpgradeNeeded : Just $ \_ _ index -> launchAff' do
761− key <- IDBIndex.getKey index (IDBKeyRange.only "patate")
762− key `shouldEqual` (Just $ toKey 1)
763−
764− key <- IDBIndex.getKey index (IDBKeyRange.only "autruche")
765− key `shouldEqual` (Just $ toKey 1)
766−
767− key <- IDBIndex.getKey index (IDBKeyRange.only "bob")
768− key `shouldEqual` (Just $ toKey 2)
769− }
770− tearDown db
771−
772−
773− it "get() - returns the record with the first key in the range" do
774− { db } <- setup
775− { storeParams : { keyPath: ["key"], autoIncrement: false }
776− , indexParams : IDBIndex.defaultParameters
777− , keyPath : ["indexedProperty"]
778− , values : [ { key: 14, indexedProperty: "patate" } :+: Nothing
779− , { key: 42, indexedProperty: "autruche" } :+: Nothing
780− , { key: 1337, indexedProperty: "baguette" } :+: Nothing
781− ]
782− , onUpgradeNeeded : Nothing
783− }
784−
785− tx <- IDBDatabase.transaction db ["store"] ReadOnly
786− store <- IDBTransaction.objectStore tx "store"
787− index <- IDBObjectStore.index store "index"
788− val <- IDBIndex.get index (IDBKeyRange.lowerBound "autruche" false)
789− ((\r -> r.key) <$> val) `shouldEqual` (Just 42)
790−
791− tearDown db
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
1141+
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"