git.iko.soy/purescript-indexed-DB
on master help sign in
32 files changed+2630−2375
since splitsnapshots
unifiedsplit
.gitignore+2 README.md+64−10 bower.json−38 deploy.sh−3 index.js+4 karma.browserstack.conf.js−91 karma.conf.js−25 package.json+9−36 packages.dhall+105 spago.dhall+42 src/Database/IndexedDB/Core.js+6−6 src/Database/IndexedDB/Core.purs+143−95 src/Database/IndexedDB/IDBCursor.js+63−27 src/Database/IndexedDB/IDBCursor.purs+111−162 src/Database/IndexedDB/IDBDatabase.js+62−28 src/Database/IndexedDB/IDBDatabase.purs+132−132 src/Database/IndexedDB/IDBFactory.js+21−8 src/Database/IndexedDB/IDBFactory.purs+36−32 src/Database/IndexedDB/IDBIndex/Internal.js → src/Database/IndexedDB/IDBIndex.js+52−28 src/Database/IndexedDB/IDBIndex.purs+197−7 src/Database/IndexedDB/IDBIndex/Internal.purs−256 src/Database/IndexedDB/IDBKey.purs+8−1 src/Database/IndexedDB/IDBKey/Internal.js+3−3 src/Database/IndexedDB/IDBKey/Internal.purs+96−114 src/Database/IndexedDB/IDBKeyRange.js+9−9 src/Database/IndexedDB/IDBKeyRange.purs+91−75 src/Database/IndexedDB/IDBObjectStore.js+55−28 src/Database/IndexedDB/IDBObjectStore.purs+160−156 src/Database/IndexedDB/IDBTransaction.js+38−18 src/Database/IndexedDB/IDBTransaction.purs+66−83 test/Main.purs+974−904 yarn.lock+81
.gitignore+2
@@ -77,3 +77,5 @@ releases
7777.purs*
7878.psa*
7979dist
80+.spago
81+test.js
README.md+64−10
@@ -1,13 +1,40 @@
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)
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)
22=====
33
44This package offers complete bindings and type-safety upon the [IndexedDB API](https://w3c.github.io/IndexedDB).
55
66## Overview
77
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.
11+
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` ...)
14+
15+Here's a quick example of what it look likes.
816```purescript
17+module Main where
18+
19+import Prelude
20+
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)
26+
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
34+
35+
936main :: Eff (idb :: IDB, exception :: EXCEPTION, console :: CONSOLE) Unit
10−main = launchAff' do
37+main = launchAff_ do
1138 db <- IDBFactory.open "db" Nothing { onBlocked : Nothing
1239 , onUpgradeNeeded : Just onUpgradeNeeded
1340 }
@@ -18,23 +45,50 @@ main = launchAff' do
1845 log $ maybe "not found" id val
1946
2047
21−onUpgradeNeeded :: forall e. Database -> Transaction -> Eff (idb :: IDB, exception :: EXCEPTION | e) Unit
22−onUpgradeNeeded db _ = launchAff' do
23− store <- IDBDatabase.createObjectStore db "store" IDBObjectStore.defaultParameters
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
2451 _ <- IDBObjectStore.add store "patate" (Just 1)
2552 _ <- IDBObjectStore.add store { property: 42 } (Just 2)
26− _ <- IDBObjectStore.createIndex store "index" ["property"] IDBIndex.defaultParameters
53+ _ <- IDBObjectStore.createIndex store "index" ["property"] IDBObjectStore.defaultParameters
2754 pure unit
2855```
2956
57+## Notes
58+
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.
63+
64+### Examples
65+The `test` folder contains a great amount of examples showing practical usage of the IDB\*
66+interfaces. Do not hesitate to have a peek should you wonder how to use one of the module. The
67+wrapper tries to keep as much as possible an API consistent with the original IndexedDB API.
68+Hence, it should be quite straightforward to translate any JavaScript example to a PureScript
69+one.
70+
3071## Changelog
3172
32−- Release incoming
73+#### v3.0.0
74+
75+- callback to `onUpgradeNeeded` event now provide a record with the database old version.
76+
77+#### v2.0.0
78+
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.
81+
82+- make the Key more opaque (by having an IDBKey instance for Foreign types)
83+
84+- Upgrade purescript-exceptions to 3.1.0 and leverage the new `name` accessor
85+
3386
34−#### TODO
87+#### v1.0.0
3588
36−- Add support for `index.getAll` method
37−- Complete the specifications with the [official tests list](https://github.com/w3c/web-platform-tests/blob/master/IndexedDB/README.md) provided by W3C
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
3892
3993## Documentation
4094
bower.jsondeleted−38
@@ -1,38 +0,0 @@
1−{
2− "name": "purescript-indexeddb",
3− "description": "An API wrapper around IndexedDB",
4− "version": "1.0.0",
5− "authors": [
6− "Matthias Benkort <matthias.benkort@truqu.com>"
7− ],
8− "ignore": [
9− "**/.*",
10− "node_modules",
11− "bower_components",
12− "output",
13− "test",
14− "dist",
15− "releases",
16− "package.json",
17− "karma.conf.js",
18− "karma.browserstack.conf.js",
19− "deploy.sh"
20− ],
21− "license": "MIT",
22− "homepage": "https://github.com/truqu/purescript-indexeddb",
23− "repository": {
24− "type": "git",
25− "url": "git://github.com/KtorZ/purescript-ifrit.git"
26− },
27− "dependencies": {
28− "purescript-maybe": "^3.0.0",
29− "purescript-aff": "^3.1.0",
30− "purescript-datetime": "^3.2.0"
31− },
32− "devDependencies": {
33− "purescript-spec": "^1.0.0",
34− "purescript-psci-support": "^3.0.0",
35− "purescript-now": "^3.0.0",
36− "purescript-spec-mocha": "https://github.com/truqu/purescript-spec-mocha.git#0.5.0"
37− }
38−}
deploy.shdeleted−3
@@ -1,3 +0,0 @@
1−#!/bin/sh
2−
3−test -n "$TRAVIS_TAG" && ( yes | pulp publish --no-push )
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.browserstack.conf.jsdeleted−91
@@ -1,91 +0,0 @@
1−module.exports = (config) => {
2− config.set({
3− browsers: [
4− 'FirefoxMAC',
5− 'ChromeMAC',
6− // 'SafariMAC',
7− 'OperaMAC',
8−
9− 'FirefoxWIN',
10− 'ChromeWIN',
11− 'OperaWIN',
12− ],
13− files: [
14− 'dist/karma/index.js',
15− ],
16− frameworks: [
17− 'mocha',
18− ],
19− plugins: [
20− 'karma-browserstack-launcher',
21− 'karma-mocha',
22− ],
23− reporters: [
24− 'dots',
25− 'BrowserStack',
26− ],
27− singleRun: true,
28− client: {
29− mocha: {
30− timeout: 10000,
31− },
32− },
33− browserStack: {
34− username: process.env.BROWSERSTACK_USERNAME,
35− accessKey: process.env.BROWSERSTACK_ACCESSKEY,
36− timeout: 1500,
37− captureTimeout: 500,
38− },
39− customLaunchers: {
40− FirefoxMAC: {
41− base: 'BrowserStack',
42− browser: 'Firefox',
43− browser_version: '51',
44− os: 'OS X',
45− os_version: 'Sierra',
46− },
47− ChromeMAC: {
48− base: 'BrowserStack',
49− browser: 'Chrome',
50− browser_version: '58',
51− os: 'OS X',
52− os_version: 'Sierra',
53− },
54− SafariMAC: {
55− base: 'BrowserStack',
56− browser: 'Safari',
57− browser_version: '10.1',
58− os: 'OS X',
59− os_version: 'Sierra',
60− },
61− OperaMAC: {
62− base: 'BrowserStack',
63− browser: 'Opera',
64− browser_version: '46',
65− os: 'OS X',
66− os_version: 'Sierra',
67− },
68− FirefoxWIN: {
69− base: 'BrowserStack',
70− browser: 'Firefox',
71− browser_version: '51',
72− os: 'WINDOWS',
73− os_version: '10',
74− },
75− ChromeWIN: {
76− base: 'BrowserStack',
77− browser: 'Chrome',
78− browser_version: '58',
79− os: 'WINDOWS',
80− os_version: '10',
81− },
82− OperaWIN: {
83− base: 'BrowserStack',
84− browser: 'Opera',
85− browser_version: '46',
86− os: 'WINDOWS',
87− os_version: '10',
88− },
89− },
90− });
91−};
karma.conf.jsdeleted−25
@@ -1,25 +0,0 @@
1−module.exports = config => {
2− config.set({
3− autoWatch: true,
4− singleRun: true,
5− browsers: ["Chrome"],
6− files: [
7− "dist/karma/index.js",
8− ],
9− frameworks: [
10− "mocha",
11− ],
12− plugins: [
13− "karma-chrome-launcher",
14− "karma-firefox-launcher",
15− "karma-spec-reporter",
16− "karma-mocha",
17− ],
18− reporters: ["spec"],
19− client: {
20− mocha: {
21− timeout: 10000
22− }
23− }
24− });
25−};
package.json+9−36
@@ -1,42 +1,15 @@
11{
2− "name": "purescript-indexeddb",
2+ "name": "purescript-indexed-DB",
33 "version": "1.0.0",
4− "description": "A wrapper around the IndexedDB API",
4+ "main": "index.js",
5+ "repository": "https://github.com/ilyakooo0/purescript-indexed-DB.git",
6+ "author": "iko <mail@iko.soy>",
7+ "license": "MIT",
58 "scripts": {
6− "prepare:release": "mkdir -p releases/github && cp -r README.md LICENSE src bower.json releases/github",
7− "test:build": "mkdir -p dist/karma && pulp browserify -j 4 --main \"Test.Main\" -I test --to dist/karma/index.js",
8− "test:run": "karma start karma.conf.js",
9− "test:run:browserstack": "karma start karma.browserstack.conf.js",
10− "test:browserstack": "npm run test:build && npm run test:run:browserstack",
11− "test": "npm run test:build && npm run test:run",
12− "start": "find . -type f -regex '.*\\(src\\|test\\).*' ! -regex '.*bower_components.*\\|.*node_modules.*|.*\\.sw.*' | entr -s 'npm run test -s'"
13− },
14− "dependencies": {},
15− "devDependencies": {
16− "bower": "1.8.0",
17− "eslint": "3.19.0",
18− "eslint-config-airbnb": "15.0.1",
19− "eslint-plugin-import": "2.5.0",
20− "eslint-plugin-jsx-a11y": "5.0.3",
21− "eslint-plugin-react": "7.1.0",
22− "karma": "1.7.0",
23− "karma-browserstack-launcher": "1.3.0",
24− "karma-chrome-launcher": "2.1.1",
25− "karma-firefox-launcher": "1.0.1",
26− "karma-mocha": "1.3.0",
27− "karma-spec-reporter": "0.0.31",
28− "mocha": "3.4.2",
29− "pulp": "11.0.0",
30− "purescript": "0.11.5"
9+ "test": "spago build && node index.js"
3110 },
32− "repository": {
33− "type": "git",
34− "url": "git+https://github.com/Truqu/purescript-indexeddb.git"
35− },
36− "author": "Matthias Benkort <matthias.benkort@truqu.com>",
37− "license": "MIT",
38− "bugs": {
39− "url": "https://github.com/Truqu/purescript-indexeddb/issues"
11+ "dependencies": {
12+ "fake-indexeddb": "^4.0.1"
4013 },
41− "homepage": "https://github.com/Truqu/purescript-indexeddb#readme"
14+ "type": "module"
4215}
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+143−95
@@ -1,39 +1,72 @@
11-- | The Core module gathers types used across the library and provides basic Show instances for
22-- | those types.
3−-- |
4−-- | NOTE:
5−-- | Errors normally thrown by the IDB* interfaces are wrapped in the `Aff` Monad as `Error`
6−-- | where the `message` corresponds to the error's name (e.g. "InvalidStateError").
7−-- | Pattern matching can therefore be done on any error message to handle specific errors thrown
8−-- | by the API.
9−
103module Database.IndexedDB.Core
11− ( class FromString, parse
12− , IDB
13− , CursorDirection(..)
14− , CursorSource(..)
15− , Database
4+ -- * Effects
5+ (
6+ -- IDB
7+
8+ -- * Types
9+ Database
1610 , Index
1711 , KeyCursor
1812 , KeyRange
19− , KeyPath
2013 , ObjectStore
2114 , Transaction
22− , TransactionMode(..)
2315 , 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
2431 , module Database.IndexedDB.IDBKey
2532 ) where
2633
27−import Prelude (class Show)
34+import Prelude (class Show)
2835
29−import Control.Monad.Eff (kind Effect)
30−import Data.Maybe (Maybe(..))
36+import Data.Maybe (Maybe(..))
37+import Data.String.Read (class Read)
3138
3239import Database.IndexedDB.IDBKey
3340
34−-- | IDB Effects, manifestation that something happened with the IndexedDB
35−foreign import data IDB :: Effect
41+--------------------
42+-- TYPES
43+--
44+
45+-- | Each origin has an associated set of databases. A database has zero or more object
46+-- | stores which hold the data stored in the database.
47+foreign import data Database :: Type
48+
49+-- | An index allows looking up records in an object store using properties of the values
50+-- | in the object stores records.
51+foreign import data Index :: Type
52+
53+-- | A key range is a continuous interval over some data type used for keys.
54+foreign import data KeyRange :: Type
55+
56+-- | A cursor is used to iterate over a range of records in an index or an object store
57+-- | in a specific direction. A KeyCursor doesn't hold any value.
58+foreign import data KeyCursor :: Type
3659
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
63+
64+-- | An object store is the primary storage mechanism for storing data in a database.
65+foreign import data ObjectStore :: Type
66+
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
3770
3871-- | A cursor has a direction that determines whether it moves in monotonically
3972-- | increasing or decreasing order of the record keys when iterated, and if it
@@ -46,7 +79,6 @@ data CursorDirection
4679 | Prev
4780 | PrevUnique
4881
49−
5082-- | If the source of a cursor is an object store, the effective object store of
5183-- | the cursor is that object store and the effective key of the cursor is the
5284-- | cursor’s position. If the source of a cursor is an index, the effective object
@@ -56,17 +88,6 @@ data CursorSource
5688 = ObjectStore ObjectStore
5789 | Index Index
5890
59−
60−-- | A transaction has a mode that determines which types of interactions can be performed
61−-- | upon that transaction. The mode is set when the transaction is created and remains
62−-- | fixed for the life of the transaction.
63−data TransactionMode
64− = ReadOnly -- ^ The transaction is only allowed to read data.
65− | ReadWrite -- ^ The transaction is allowed to read, modify and delete data from existing object stores
66− | 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.
67−
68−
69−
7091-- | A key path is a list of strings that defines how to extract a key from a value.
7192-- | A valid key path is one of:
7293-- |
@@ -76,119 +97,146 @@ data TransactionMode
7697-- | - A non-empty list containing only strings conforming to the above requirements.
7798type KeyPath = Array String
7899
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.
79107
80−-- | Each origin has an associated set of databases. A database has zero or more object
81−-- | stores which hold the data stored in the database.
82−foreign import data Database :: Type
108+--------------------
109+-- Classes
110+--
83111
84−-- | An index allows looking up records in an object store using properties of the values
85−-- | in the object stores records.
86−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
87115
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
88120
89−-- | A cursor is used to iterate over a range of records in an index or an object store
90−-- | in a specific direction. A KeyCursor doesn't hold any value.
91−foreign import data KeyCursor :: Type
121+-- | The IDBDatabase interface represents a connection to a database.
122+class IDBDatabase db
92123
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
93128
94−-- | A key range is a continuous interval over some data type used for keys.
95−foreign import data KeyRange :: Type
129+-- | The IDBKeyRange interface represents a key range.
130+class IDBKeyRange range
96131
132+-- | The IDBtransaction interface.
133+class IDBTransaction tx
97134
98−-- | An object store is the primary storage mechanism for storing data in a database.
99−foreign import data ObjectStore :: Type
135+-- | The IDBObjectStore interface represents an object store handle.
136+class IDBObjectStore store
100137
138+--------------------
139+-- INSTANCES
140+--
101141
102−-- | A Transaction is used to interact with the data in a database.
103−-- | Whenever data is read or written to the database it is done by using a transaction.
104−foreign import data Transaction :: Type
142+instance idbCursorKeyCursor :: IDBCursor KeyCursor
105143
144+instance idbCursorValueCursor :: IDBCursor ValueCursor
106145
107−-- | A cursor is used to iterate over a range of records in an index or an object store
108−-- | in a specific direction. A ValueCursor also holds the value corresponding to matching key.
109−foreign import data ValueCursor :: Type
146+instance idbConcreteCursorKeyCursor :: IDBConcreteCursor KeyCursor
110147
148+instance idbConcreteCursorValueCursor :: IDBConcreteCursor ValueCursor
111149
112−-- | FromString represents enumerations that can be represented as Strings.
113−class FromString a where
114− parse :: String -> Maybe a
150+instance idbDatabaseDatabase :: IDBDatabase Database
115151
152+instance idbIndexIndex :: IDBIndex Index
116153
117−foreign import _showCursor :: forall cursor. cursor -> String
118−instance showKeyCursor :: Show KeyCursor where
119− show = _showCursor
154+instance idbIndexObjectStore :: IDBIndex ObjectStore
120155
156+instance idbKeyRangeKeyRange :: IDBKeyRange KeyRange
121157
122−instance showValueCursor :: Show ValueCursor where
123− show = _showCursor
158+instance idbObjectStoreObjectStore :: IDBObjectStore ObjectStore
124159
160+instance idbTransactionTransaction :: IDBTransaction Transaction
125161
126−foreign import _showDatabase
127− :: Database
128− -> String
129−instance showDatabase :: Show Database where
130− show = _showDatabase
162+instance showKeyCursor :: Show KeyCursor where
163+ show = _showCursor
131164
165+instance showValueCursor :: Show ValueCursor where
166+ show = _showCursor
132167
133−foreign import _showIndex
134− :: Index
135− -> String
136168instance showIndex :: Show Index where
137169 show = _showIndex
138170
139−
140−foreign import _showKeyRange
141− :: KeyRange
142− -> String
143171instance showKeyRange :: Show KeyRange where
144172 show = _showKeyRange
145173
146−
147−foreign import _showObjectStore
148− :: ObjectStore
149− -> String
150174instance showObjectStore :: Show ObjectStore where
151175 show = _showObjectStore
152176
153−
154−foreign import _showTransaction
155− :: Transaction
156− -> String
157177instance showTransaction :: Show Transaction where
158178 show = _showTransaction
159179
180+instance showDatabase :: Show Database where
181+ show = _showDatabase
160182
161183instance showCursorDirection :: Show CursorDirection where
162184 show x =
163185 case x of
164− Next -> "next"
186+ Next -> "next"
165187 NextUnique -> "nextunique"
166− Prev -> "prev"
188+ Prev -> "prev"
167189 PrevUnique -> "prevunique"
168190
169−
170191instance showTransactionMode :: Show TransactionMode where
171192 show x =
172193 case x of
173− ReadOnly -> "readonly"
174− ReadWrite -> "readwrite"
194+ ReadOnly -> "readonly"
195+ ReadWrite -> "readwrite"
175196 VersionChange -> "versionchange"
176197
177−
178−instance fromStringCursorDirection :: FromString CursorDirection where
179− parse s =
198+instance readCursorDirection :: Read CursorDirection where
199+ read s =
180200 case s of
181− "next" -> Just Next
201+ "next" -> Just Next
182202 "nextunique" -> Just NextUnique
183− "prev" -> Just Prev
203+ "prev" -> Just Prev
184204 "prevunique" -> Just PrevUnique
185− _ -> Nothing
186−
205+ _ -> Nothing
187206
188−instance fromStringTransactionMode :: FromString TransactionMode where
189− parse s =
207+instance readTransactionMode :: Read TransactionMode where
208+ read s =
190209 case s of
191− "readonly" -> Just ReadOnly
192− "readwrite" -> Just ReadWrite
210+ "readonly" -> Just ReadOnly
211+ "readwrite" -> Just ReadWrite
193212 "versionchange" -> Just VersionChange
194− _ -> 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+63−27
@@ -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,99 +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);
2932 success();
3033 } catch (e) {
31− error(new Error(e.name));
34+ error(e);
3235 }
36+
37+ return function canceler(_, cancelerError) {
38+ cancelerError(new Error("Can't cancel IDB Effects"));
39+ };
3340 };
3441};
3542
36−exports._continuePrimaryKey = function _continuePrimaryKey(cursor, key, primaryKey) {
37− return function aff(success, error) {
43+export function _continuePrimaryKey(cursor, key, primaryKey) {
44+ return function aff(error, success) {
3845 try {
3946 cursor.continuePrimaryKey(key, primaryKey);
4047 success();
4148 } catch (e) {
42− error(new Error(e.name));
49+ error(e);
4350 }
51+
52+ return function canceler(_, cancelerError) {
53+ cancelerError(new Error("Can't cancel IDB Effects"));
54+ };
4455 };
4556};
4657
47−exports._delete = function _delete(cursor) {
48− return function aff(success, error) {
58+export function _delete(cursor) {
59+ return function aff(error, success) {
4960 try {
5061 const request = cursor.delete();
5162 request.onsuccess = successHandler(success);
5263 request.onerror = errorHandler(error);
5364 } catch (e) {
54− error(new Error(e.name));
65+ error(e);
5566 }
67+
68+ return function canceler(_, cancelerError) {
69+ cancelerError(new Error("Can't cancel IDB Effects"));
70+ };
5671 };
5772};
5873
59−exports._direction = function _direction(fromString, cursor) {
74+export function _direction(fromString, cursor) {
6075 return fromString(cursor.direction);
6176};
6277
63−exports._key = function _key(cursor) {
64− return function aff(success, error) {
78+export function _key(cursor) {
79+ return function aff(error, success) {
6580 try {
6681 success(cursor.key);
6782 } catch (e) {
68− error(new Error(e.name));
83+ error(e);
6984 }
85+
86+ return function canceler(_, cancelerError) {
87+ cancelerError(new Error("Can't cancel IDB Effects"));
88+ };
7089 };
7190};
7291
73−exports._primaryKey = function _primaryKey(cursor) {
74− return function aff(success, error) {
92+export function _primaryKey(cursor) {
93+ return function aff(error, success) {
7594 try {
7695 success(cursor.primaryKey);
7796 } catch (e) {
78− error(new Error(e.name));
97+ error(e);
7998 }
99+
100+ return function canceler(_, cancelerError) {
101+ cancelerError(new Error("Can't cancel IDB Effects"));
102+ };
80103 };
81104};
82105
83−exports._source = function _source(IDBObjectStore, IDBIndex, cursor) {
106+export function _source(IDBObjectStore, IDBIndex, cursor) {
84107 switch (cursor.source.constructor.name) {
85108 case 'IDBIndex':
86109 return IDBIndex(cursor.source);
87110 case 'IDBObjectStore':
88111 return IDBObjectStore(cursor.source);
89112 default:
90− 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+ });
91123 }
92124};
93125
94−exports._update = function _update(cursor, value) {
95− return function aff(success, error) {
126+export function _update(cursor, value) {
127+ return function aff(error, success) {
96128 try {
97129 const request = cursor.update(value);
98130 request.onsuccess = successHandler(success);
99131 request.onerror = errorHandler(error);
100132 } catch (e) {
101− error(new Error(e.name));
133+ error(e);
102134 }
135+
136+ return function canceler(_, cancelerError) {
137+ cancelerError(new Error("Can't cancel IDB Effects"));
138+ };
103139 };
104140};
105141
106−exports._value = function _value(cursor) {
142+export function _value(cursor) {
107143 return cursor.value;
108144};
src/Database/IndexedDB/IDBCursor.purs+111−162
@@ -1,237 +1,186 @@
11-- | A cursor is used to iterate over a range of records in an index or
22-- | an object store in a specific direction.
33module Database.IndexedDB.IDBCursor
4− ( class IDBCursor, advance, continue, continuePrimaryKey, delete, update
4+ -- * Interface
5+ ( advance
6+ , continue
7+ , continuePrimaryKey
8+ , delete
9+ , update
10+
11+ -- * Attributes
512 , direction
613 , key
714 , primaryKey
815 , source
9− , direction'
10− , key'
11− , primaryKey'
12− , source'
1316 , value
1417 ) where
1518
16−import Prelude (Unit, ($), (>>>), map)
17−
18−import Control.Monad.Aff (Aff)
19−import Data.Function.Uncurried as Fn
20−import Data.Function.Uncurried (Fn2, Fn3)
21−import Data.Maybe (Maybe)
22−import Data.Nullable (Nullable, toNullable)
23−import Data.Foreign (Foreign, toForeign, unsafeFromForeign)
24−
2519import Database.IndexedDB.Core
26−import Database.IndexedDB.IDBKey.Internal (class IDBKey, Key(..), toKey, extractForeign)
2720
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)
2831
2932--------------------
3033-- INTERFACES
3134--
32−-- | Cursor objects implement the IDBCursor interface.
33−-- | There is only ever one IDBCursor instance representing a given cursor.
34−-- | There is no limit on how many cursors can be used at the same time.
35−class IDBCursor cursor where
36− -- | Advances the cursor through the next count records in range.
37− advance
38− :: forall e
39− . cursor
40− -> Int
41− -> Aff (idb :: IDB | e) Unit
42−
43− -- | Advances the cursor to the next record in range matching or after key.
44− continue
45− :: forall e k. (IDBKey k)
46− => cursor
47− -> Maybe k
48− -> Aff (idb :: IDB | e) Unit
49−
50− -- | 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.
51− continuePrimaryKey
52− :: forall e k. (IDBKey k)
53− => cursor
54− -> k
55− -> k
56− -> Aff (idb :: IDB | e) Unit
57−
58− -- | Delete the record pointed at by the cursor with a new value.
59− delete
60− :: forall e
61− . cursor
62− -> Aff (idb :: IDB | e) Unit
63−
64− -- | Update the record pointed at by the cursor with a new value.
65− -- |
66− -- | Throws a "DataError" DOMException if the effective object store uses
67− -- | in-line keys and the key would have changed.
68− update
69− :: forall val e
70− . cursor
71− -> val
72− -> Aff (idb :: IDB | e) Key
7335
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
7490
7591--------------------
7692-- ATTRIBUTES
7793--
78−-- | Returns the direction (Next|NextUnique|Prev|PrevUnique) of the cursor.
79−direction'
80− :: KeyCursor
81− -> CursorDirection
82−direction' =
83− Fn.runFn2 _direction (parse >>> toNullable)
84−
85−
86−-- | Returns the key of the cursor. Throws a "InvalidStateError" DOMException
87−-- | if the cursor is advancing or is finished.
88−key'
89− :: forall e
90− . KeyCursor
91− -> Aff (idb :: IDB | e) Key
92−key' =
93− _key >>> map Key
94−
95−
96−-- | Returns the effective key of the cursor. Throws a "InvalidStateError" DOMException
97−-- | if the cursor is advancing or is finished.
98−primaryKey'
99− :: forall e
100− . KeyCursor
101− -> Aff (idb :: IDB | e) Key
102−primaryKey' =
103− _primaryKey >>> map Key
104−
105−
106−-- | Returns the IDBObjectStore or IDBIndex the cursor was opened from.
107−source'
108− :: KeyCursor
109− -> CursorSource
110−source' =
111− Fn.runFn3 _source ObjectStore Index
112−
113−
114−-- NOTE: Polymorphism not used here cause *we want* those methods to be distincs for
115−-- the concrete types mostly for consistency with the Database.IndexedDB.IDBIndex module
116−-- where the IDBIndex interfaces is shared by Index and ObjectStore, but attributes of
117−-- respective concrete types aren't shared.
118−
11994
12095-- | Returns the direction (Next|NextUnique|Prev|PrevUnique) of the cursor.
12196direction
122− :: ValueCursor
97+ :: forall cursor
98+ . (IDBConcreteCursor cursor)
99+ => cursor
123100 -> CursorDirection
124101direction =
125− Fn.runFn2 _direction (parse >>> toNullable)
126−
102+ Fn.runFn2 _direction (read >>> toNullable)
127103
128104-- | Returns the key of the cursor. Throws a "InvalidStateError" DOMException
129105-- | if the cursor is advancing or is finished.
130106key
131− :: forall e
132− . ValueCursor
133− -> Aff (idb :: IDB | e) Key
107+ :: forall cursor
108+ . (IDBConcreteCursor cursor)
109+ => cursor
110+ -> Aff Key
134111key =
135− _key >>> map Key
136−
112+ map toKey <<< fromEffectFnAff <<< _key
137113
138114-- | Returns the effective key of the cursor. Throws a "InvalidStateError" DOMException
139115-- | if the cursor is advancing or is finished.
140116primaryKey
141− :: forall e
142− . ValueCursor
143− -> Aff (idb :: IDB | e) Key
117+ :: forall cursor
118+ . (IDBConcreteCursor cursor)
119+ => cursor
120+ -> Aff Key
144121primaryKey =
145− _primaryKey >>> map Key
146−
122+ map toKey <<< fromEffectFnAff <<< _primaryKey
147123
148124-- | Returns the IDBObjectStore or IDBIndex the cursor was opened from.
149125source
150− :: ValueCursor
126+ :: forall cursor
127+ . (IDBConcreteCursor cursor)
128+ => cursor
151129 -> CursorSource
152130source =
153131 Fn.runFn3 _source ObjectStore Index
154132
155−
156133value
157134 :: forall val
158− . ValueCursor
135+ . ValueCursor
159136 -> val
160137value =
161138 _value >>> unsafeFromForeign
162139
163−
164−--------------------
165−-- INSTANCES
166−--
167−instance keyCursorKeyCursor :: IDBCursor KeyCursor where
168− advance = Fn.runFn2 _advance
169− continue c mk = Fn.runFn2 _continue c (toNullable $ map (toKey >>> extractForeign) mk)
170− continuePrimaryKey c k1 k2 = Fn.runFn3 _continuePrimaryKey c (extractForeign $ toKey k1) (extractForeign $ toKey k2)
171− delete = _delete
172− update c = toForeign >>> Fn.runFn2 _update c >>> map Key
173−
174−
175−instance valueCursorKeyCursor :: IDBCursor ValueCursor where
176− advance = Fn.runFn2 _advance
177− continue c mk = Fn.runFn2 _continue c (toNullable $ map (toKey >>> extractForeign) mk)
178− continuePrimaryKey c k1 k2 = Fn.runFn3 _continuePrimaryKey c (extractForeign $ toKey k1) (extractForeign $ toKey k2)
179− delete = _delete
180− update c = toForeign >>> Fn.runFn2 _update c >>> map Key
181−
182−
183140--------------------
184141-- FFI
185142--
186−foreign import _advance
187− :: forall cursor e
188− . Fn2 cursor Int (Aff (idb :: IDB | e) Unit)
189143
144+foreign import _advance
145+ :: forall cursor
146+ . Fn2 cursor Int (EffectFnAff Unit)
190147
191148foreign import _continue
192− :: forall cursor e
193− . Fn2 cursor (Nullable Foreign) (Aff (idb :: IDB | e) Unit)
194−
149+ :: forall cursor
150+ . Fn2 cursor (Nullable Foreign) (EffectFnAff Unit)
195151
196152foreign import _continuePrimaryKey
197− :: forall cursor e
198− . Fn3 cursor Foreign Foreign (Aff (idb :: IDB | e) Unit)
199−
153+ :: forall cursor
154+ . Fn3 cursor Foreign Foreign (EffectFnAff Unit)
200155
201156foreign import _delete
202− :: forall cursor e
203− . cursor
204− -> (Aff (idb :: IDB | e) Unit)
205−
157+ :: forall cursor
158+ . cursor
159+ -> (EffectFnAff Unit)
206160
207161foreign import _direction
208162 :: forall cursor
209− . Fn2 (String -> Nullable CursorDirection) cursor CursorDirection
210−
163+ . Fn2 (String -> Nullable CursorDirection) cursor CursorDirection
211164
212165foreign import _key
213− :: forall cursor e
214− . cursor
215− -> Aff (idb :: IDB | e) Foreign
216−
166+ :: forall cursor
167+ . cursor
168+ -> EffectFnAff Key
217169
218170foreign import _primaryKey
219− :: forall cursor e
220− . cursor
221− -> Aff (idb :: IDB | e) Foreign
222−
171+ :: forall cursor
172+ . cursor
173+ -> EffectFnAff Key
223174
224175foreign import _source
225176 :: forall cursor
226− . Fn3 (ObjectStore -> CursorSource) (Index -> CursorSource) cursor CursorSource
227−
177+ . Fn3 (ObjectStore -> CursorSource) (Index -> CursorSource) cursor CursorSource
228178
229179foreign import _update
230− :: forall cursor e
231− . Fn2 cursor Foreign (Aff (idb :: IDB | e) Foreign)
232−
180+ :: forall cursor
181+ . Fn2 cursor Foreign (EffectFnAff Foreign)
233182
234183foreign import _value
235184 :: forall cursor val
236− . cursor
185+ . cursor
237186 -> 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+132−132
@@ -1,215 +1,215 @@
11-- | Each origin has an associated set of databases. A database has zero or more object
22-- | stores which hold the data stored in the database.
33module Database.IndexedDB.IDBDatabase
4− ( class IDBDatabase, close, createObjectStore, deleteObjectStore, transaction
5− , StoreName
4+ -- * Types
5+ ( StoreName
6+ , ObjectStoreParameters
7+ , defaultParameters
8+
9+ -- * Interface
10+ , close
11+ , createObjectStore
12+ , deleteObjectStore
13+ , transaction
14+
15+ -- * Attributes
616 , name
717 , objectStoreNames
818 , version
19+
20+ -- * Event handlers
921 , onAbort
1022 , onClose
1123 , onError
1224 , onVersionChange
1325 ) where
1426
15−import Prelude (Unit, show)
16−
17−import Control.Monad.Aff (Aff)
18−import Control.Monad.Eff (Eff)
19−import Control.Monad.Eff.Exception (Error)
20−import Data.Function.Uncurried as Fn
21−import Data.Function.Uncurried (Fn2, Fn3)
22−
2327import Database.IndexedDB.Core
24−import Database.IndexedDB.IDBObjectStore (IDBObjectStoreParameters)
2528
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, (<<<), ($))
2636
2737--------------------
28−-- INTERFACE
38+-- TYPES
2939--
30−-- | The IDBDatabase interface represents a connection to a database.
31−class IDBDatabase db where
32− -- | Closes the connection once all running transactions have finished.
33− close
34− :: forall e
35− . db
36− -> Aff (idb :: IDB | e) Unit
37−
38− -- | Creates a new object store with the given name and options and returns a new IDBObjectStore.
39− -- |
40− -- | Throws a "InvalidStateError" DOMException if not called within an upgrade transaction
41− createObjectStore
42− :: forall e
43− . db
44− -> StoreName
45− -> IDBObjectStoreParameters
46− -> Aff (idb :: IDB | e) ObjectStore
47−
48− -- | Deletes the object store with the given name.
49− -- |
50− -- | Throws a "InvalidStateError" DOMException if not called within an upgrade transaction.
51− deleteObjectStore
52− :: forall e
53− . db
54− -> StoreName
55− -> Aff (idb :: IDB | e) ObjectStore
56−
57− -- | Returns a new transaction with the given mode (ReadOnly|ReadWrite)
58− -- | and scope which in the form of an array of object store names.
59− transaction
60− :: forall e
61− . db
62− -> Array StoreName
63− -> TransactionMode
64− -> Aff (idb :: IDB | e) Transaction
65−
6640
6741-- | Type alias for StoreName
6842type StoreName = String
6943
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')
70105
71106--------------------
72107-- ATTRIBUTES
73108--
109+
74110-- | Returns the name of the database.
75111name
76− :: Database
77− -> String
112+ :: Database
113+ -> String
78114name =
79115 _name
80116
81−
82117-- | Returns a list of the names of object stores in the database.
83118objectStoreNames
84− :: Database
85− -> Array String
119+ :: Database
120+ -> Array String
86121objectStoreNames =
87122 _objectStoreNames
88123
89−
90124-- | Returns the version of the database.
91125version
92− :: Database
93− -> Int
126+ :: Database
127+ -> Int
94128version =
95129 _version
96130
97−
98131--------------------
99132-- EVENT HANDLERS
100133--
101134-- | Event handler for the `abort` event.
102135onAbort
103− :: forall e e'
104− . Database
105− -> Eff ( | e') Unit
106− -> Aff (idb :: IDB | e) Unit
136+ :: Database
137+ -> Effect Unit
138+ -> Aff Unit
107139onAbort db f =
108− Fn.runFn2 _onAbort db f
109−
140+ fromEffectFnAff $ Fn.runFn2 _onAbort db f
110141
111142-- | Event handler for the `close` event.
112143onClose
113− :: forall e e'
114− . Database
115− -> Eff ( | e') Unit
116− -> Aff (idb :: IDB | e) Unit
144+ :: Database
145+ -> Effect Unit
146+ -> Aff Unit
117147onClose db f =
118− Fn.runFn2 _onClose db f
119−
148+ fromEffectFnAff $ Fn.runFn2 _onClose db f
120149
121150-- | Event handler for the `error` event.
122151onError
123− :: forall e e'
124− . Database
125− -> (Error -> Eff ( | e') Unit)
126− -> Aff (idb :: IDB | e) Unit
152+ :: Database
153+ -> (Error -> Effect Unit)
154+ -> Aff Unit
127155onError db f =
128− Fn.runFn2 _onError db f
129−
156+ fromEffectFnAff $ Fn.runFn2 _onError db f
130157
131158-- | Event handler for the `versionchange` event.
132159onVersionChange
133− :: forall e e'
134− . Database
135− -> ({ oldVersion :: Int, newVersion :: Int }
136− -> Eff ( | e') Unit)
137− -> Aff (idb :: IDB | e) Unit
160+ :: Database
161+ -> ( { oldVersion :: Int, newVersion :: Int }
162+ -> Effect Unit
163+ )
164+ -> Aff Unit
138165onVersionChange db f =
139− Fn.runFn2 _onVersionChange db f
140−
141−
142−--------------------
143−-- INSTANCES
144−--
145−instance idbDatabaseDatabase :: IDBDatabase Database where
146− close =
147− _close
148−
149− createObjectStore db name' opts =
150− Fn.runFn3 _createObjectStore db name' opts
151−
152− deleteObjectStore db name' =
153− Fn.runFn2 _deleteObjectStore db name'
154−
155− transaction db stores mode' =
156− Fn.runFn3 _transaction db stores (show mode')
157−
166+ fromEffectFnAff $ Fn.runFn2 _onVersionChange db f
158167
159168--------------------
160169-- FFI
161170--
162−foreign import _close
163− :: forall db e
164− . db
165− -> Aff (idb :: IDB | e) Unit
166171
172+foreign import _close
173+ :: forall db
174+ . db
175+ -> EffectFnAff Unit
167176
168177foreign import _createObjectStore
169− :: forall db e
170− . Fn3 db String { keyPath :: Array String, autoIncrement :: Boolean } (Aff (idb :: IDB | e) ObjectStore)
171−
178+ :: forall db
179+ . Fn3 db String { keyPath :: Array String, autoIncrement :: Boolean } (EffectFnAff ObjectStore)
172180
173181foreign import _deleteObjectStore
174− :: forall db e
175− . Fn2 db String (Aff (idb :: IDB | e) ObjectStore)
176−
182+ :: forall db
183+ . Fn2 db String (EffectFnAff ObjectStore)
177184
178185foreign import _name
179− :: Database
180− -> String
181−
186+ :: Database
187+ -> String
182188
183189foreign import _objectStoreNames
184− :: Database
185− -> Array String
186−
190+ :: Database
191+ -> Array String
187192
188193foreign import _onAbort
189− :: forall db e e'
190− . Fn2 db (Eff ( | e') Unit) (Aff (idb :: IDB | e) Unit)
191−
194+ :: forall db
195+ . Fn2 db (Effect Unit) (EffectFnAff Unit)
192196
193197foreign import _onClose
194− :: forall db e e'
195− . Fn2 db (Eff ( | e') Unit) (Aff (idb :: IDB | e) Unit)
196−
198+ :: forall db
199+ . Fn2 db (Effect Unit) (EffectFnAff Unit)
197200
198201foreign import _onError
199− :: forall db e e'
200− . Fn2 db (Error -> Eff ( | e') Unit) (Aff (idb :: IDB | e) Unit)
201−
202+ :: forall db
203+ . Fn2 db (Error -> Effect Unit) (EffectFnAff Unit)
202204
203205foreign import _onVersionChange
204− :: forall db e e'
205− . Fn2 db ({ oldVersion :: Int, newVersion :: Int } -> Eff ( | e') Unit) (Aff (idb :: IDB | e) Unit)
206−
206+ :: forall db
207+ . Fn2 db ({ oldVersion :: Int, newVersion :: Int } -> Effect Unit) (EffectFnAff Unit)
207208
208209foreign import _transaction
209− :: forall db e
210− . Fn3 db (Array String) String (Aff (idb :: IDB | e) Transaction)
211−
210+ :: forall db
211+ . Fn3 db (Array String) String (EffectFnAff Transaction)
212212
213213foreign import _version
214− :: Database
215− -> Int
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+36−32
@@ -1,47 +1,55 @@
11-- | Database objects are accessed through methods on the IDBFactory interface.
22-- | A single object implementing this interface is present in the global scope
33-- | of environments that support Indexed DB operations.
4−module Database.IndexedDB.IDBFactory where
4+module Database.IndexedDB.IDBFactory
5+ -- * Types
6+ ( Callbacks
7+ , DatabaseName
8+ , Version
59
6−import Prelude (Unit)
7−
8−import Control.Monad.Aff (Aff)
9−import Control.Monad.Eff (Eff)
10−import Data.Function.Uncurried as Fn
11−import Data.Function.Uncurried (Fn4)
12−import Data.Maybe (Maybe, fromMaybe)
10+ -- * Interface
11+ , deleteDatabase
12+ , open
13+ ) where
1314
1415import Database.IndexedDB.Core
1516
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, ($), (<<<))
1624
1725--------------------
18−-- INTERFACE
26+-- TYPES
1927--
28+
2029-- Type alias for binding listeners to an initial open action.
21−type OpenRequest e =
22− { onBlocked :: Maybe (Eff (| e) Unit)
23− , onUpgradeNeeded :: Maybe (Database -> Transaction -> Eff (| e) Unit)
30+type Callbacks =
31+ { onBlocked :: Maybe (Effect Unit)
32+ , onUpgradeNeeded :: Maybe (Database -> Transaction -> { oldVersion :: Int } -> Effect Unit)
2433 }
2534
26−
2735-- | Type alias for DatabaseName.
2836type DatabaseName = String
2937
30−
3138-- | Type alias for Version.
3239type Version = Int
3340
41+--------------------
42+-- INTERFACE
43+--
3444
3545-- | Attempts to delete the named database. If the database already exists
3646-- | and there are open connections that don’t close in response to a
3747-- | `versionchange` event, the request will be blocked until all they close.
3848deleteDatabase
39− :: forall e
40− . DatabaseName
41− -> Aff (idb :: IDB | e) Int
49+ :: DatabaseName
50+ -> Aff Int
4251deleteDatabase =
43− _deleteDatabase
44−
52+ fromEffectFnAff <<< _deleteDatabase
4553
4654-- | Attempts to open a connection to the named database with the specified version.
4755-- | If the database already exists with a lower version and there are open connections
@@ -52,24 +60,20 @@ deleteDatabase =
5260-- | When the version isn't provided (`Nothing`), attempts to open a connection to the
5361-- | named database with the current version, or 1 if it does not already exist.
5462open
55− :: forall e e'
56− . DatabaseName
57− -> Maybe Version
58− -> OpenRequest e'
59− -> Aff (idb :: IDB | e) Database
63+ :: DatabaseName
64+ -> Maybe Version
65+ -> Callbacks
66+ -> Aff Database
6067open name mver req =
61− Fn.runFn4 _open fromMaybe name mver req
62−
68+ fromEffectFnAff $ Fn.runFn4 _open fromMaybe name mver req
6369
6470--------------------
6571-- FFI
6672--
6773foreign import _deleteDatabase
68− :: forall e
69− . String
70− -> Aff (idb :: IDB | e) Int
71−
74+ :: String
75+ -> EffectFnAff Int
7276
7377foreign import _open
74− :: forall a e e'
75− . Fn4 (a -> Maybe a -> a) String (Maybe Int) (OpenRequest e') (Aff (idb :: IDB | e) Database)
78+ :: forall a
79+ . Fn4 (a -> Maybe a -> a) String (Maybe Int) (Callbacks) (EffectFnAff Database)
src/Database/IndexedDB/IDBIndex/Internal.js → src/Database/IndexedDB/IDBIndex.jsrenamed+52−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
@@ -11,7 +11,7 @@ const successHandler = function successHandler(cb) {
1111};
1212
1313
14−exports._keyPath = function _keyPath(index) {
14+export function _keyPath(index) {
1515 const path = index.keyPath;
1616
1717 if (Array.isArray(path)) {
@@ -25,43 +25,51 @@ exports._keyPath = function _keyPath(index) {
2525 return [];
2626};
2727
28−exports._multiEntry = function _multiEntry(index) {
28+export function _multiEntry(index) {
2929 return index.multiEntry;
3030};
3131
32−exports._name = function _name(index) {
32+export function _name(index) {
3333 return index.name;
3434};
3535
36−exports._objectStore = function _objectStore(index) {
36+export function _objectStore(index) {
3737 return index.obectStore;
3838};
3939
40−exports._unique = function _unique(index) {
40+export function _unique(index) {
4141 return index.unique;
4242};
4343
44−exports._count = function _count(index, query) {
45− return function aff(success, error) {
44+export function _count(index, query) {
45+ return function aff(error, success) {
4646 try {
4747 const request = index.count(query);
4848 request.onsuccess = successHandler(success);
4949 request.onerror = errorHandler(error);
5050 } catch (e) {
51− error(new Error(e.name));
51+ error(e);
5252 }
53+
54+ return function canceler(_, cancelerError) {
55+ cancelerError(new Error("Can't cancel IDB Effects"));
56+ };
5357 };
5458};
5559
56−exports._get = function _get(index, range) {
57− return function aff(success, error) {
60+export function _get(index, range) {
61+ return function aff(error, success) {
5862 try {
5963 const request = index.get(range);
6064 request.onsuccess = successHandler(success);
6165 request.onerror = errorHandler(error);
6266 } catch (e) {
63− error(new Error(e.name));
67+ error(e);
6468 }
69+
70+ return function canceler(_, cancelerError) {
71+ cancelerError(new Error("Can't cancel IDB Effects"));
72+ };
6573 };
6674};
6775
@@ -71,8 +79,8 @@ exports._get = function _get(index, range) {
7179 *
7280 * However, it may be doable to convert the result to some key / value structure with values of
7381 * different types.
74−exports._getAll = function _getAll(index, query, count) {
75− return function aff(success, error) {
82+export function _getAll(index, query, count) {
83+ return function aff(error, success) {
7684 const request = index.getAll(query, count);
7785 request.onsuccess = successHandler(success);
7886 request.onerror = errorHandler(error);
@@ -80,32 +88,40 @@ exports._getAll = function _getAll(index, query, count) {
8088};
8189*/
8290
83−exports._getAllKeys = function _getAllKeys(index, range, count) {
84− return function aff(success, error) {
91+export function _getAllKeys(index, range, count) {
92+ return function aff(error, success) {
8593 try {
8694 const request = index.getAllKeys(range, count || undefined);
8795 request.onsuccess = successHandler(success);
8896 request.onerror = errorHandler(error);
8997 } catch (e) {
90− error(new Error(e.name));
98+ error(e);
9199 }
100+
101+ return function canceler(_, cancelerError) {
102+ cancelerError(new Error("Can't cancel IDB Effects"));
103+ };
92104 };
93105};
94106
95−exports._getKey = function _getKey(index, range) {
96− return function aff(success, error) {
107+export function _getKey(index, range) {
108+ return function aff(error, success) {
97109 try {
98110 const request = index.getKey(range);
99111 request.onsuccess = successHandler(success);
100112 request.onerror = errorHandler(error);
101113 } catch (e) {
102− error(new Error(e.name));
114+ error(e);
103115 }
116+
117+ return function canceler(_, cancelerError) {
118+ cancelerError(new Error("Can't cancel IDB Effects"));
119+ };
104120 };
105121};
106122
107−exports._openCursor = function _openCursor(index, query, dir, cb) {
108− return function aff(success, error) {
123+export function _openCursor(index, query, dir, cb) {
124+ return function aff(error, success) {
109125 try {
110126 const request = index.openCursor(query, dir);
111127 request.onsuccess = function onSuccess(e) {
@@ -116,17 +132,21 @@ exports._openCursor = function _openCursor(index, query, dir, cb) {
116132 }
117133 };
118134 request.onerror = function onError(e) {
119− cb.onError(new Error(e.target.error.name));
135+ cb.onError(e.target.error);
120136 };
121137 success();
122138 } catch (e) {
123− error(new Error(e.name));
139+ error(e);
124140 }
141+
142+ return function canceler(_, cancelerError) {
143+ cancelerError(new Error("Can't cancel IDB Effects"));
144+ };
125145 };
126146};
127147
128−exports._openKeyCursor = function _openKeyCursor(index, query, dir, cb) {
129− return function aff(success, error) {
148+export function _openKeyCursor(index, query, dir, cb) {
149+ return function aff(error, success) {
130150 try {
131151 const request = index.openKeyCursor(query, dir);
132152 request.onsuccess = function onSuccess(e) {
@@ -137,11 +157,15 @@ exports._openKeyCursor = function _openKeyCursor(index, query, dir, cb) {
137157 }
138158 };
139159 request.onerror = function onError(e) {
140− cb.onError(new Error(e.target.error.name));
160+ cb.onError(e.target.error);
141161 };
142162 success();
143163 } catch (e) {
144− error(new Error(e.name));
164+ error(e);
145165 }
166+
167+ return function canceler(_, cancelerError) {
168+ cancelerError(new Error("Can't cancel IDB Effects"));
169+ };
146170 };
147171};
src/Database/IndexedDB/IDBIndex.purs+197−7
@@ -1,16 +1,206 @@
11-- | An index allows looking up records in an object store using properties of the values
22-- | in the object stores records.
33module Database.IndexedDB.IDBIndex
4− ( module Database.IndexedDB.IDBIndex.Internal
5− ) where
4+ -- * Types
5+ ( Callbacks
6+
7+ -- * Interface
8+ , count
9+ , get
10+ , getAllKeys
11+ , getKey
12+ , openCursor
13+ , openKeyCursor
614
7−import Database.IndexedDB.IDBIndex.Internal
8− ( class IDBIndex, count, get, getAllKeys, getKey, openCursor, openKeyCursor
9− , IDBIndexParameters
15+ -- * Attributes
1016 , keyPath
1117 , multiEntry
1218 , name
1319 , objectStore
1420 , unique
15− , defaultParameters
16− )
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.pursdeleted−256
@@ -1,256 +0,0 @@
1−-- | An index allows looking up records in an object store using properties of the values
2−-- | in the object stores records.
3−module Database.IndexedDB.IDBIndex.Internal where
4−
5−import Prelude
6−
7−import Control.Monad.Aff (Aff)
8−import Control.Monad.Eff (Eff)
9−import Control.Monad.Eff.Exception (Error)
10−import Data.Foreign (Foreign, unsafeFromForeign)
11−import Data.Function.Uncurried as Fn
12−import Data.Function.Uncurried (Fn2, Fn3, Fn4)
13−import Data.Maybe (Maybe)
14−import Data.Nullable (Nullable, toMaybe, toNullable)
15−
16−import Database.IndexedDB.Core (IDB, CursorDirection, Index, Key, KeyCursor, KeyRange,
17− KeyPath, ObjectStore, ValueCursor)
18−
19−
20−--------------------
21−-- INTERFACES
22−--
23−-- | The IDBIndex interface represents an index handle.
24−-- | Any of these methods throw an "TransactionInactiveError" DOMException
25−-- | if called when the transaction is not active.
26−class IDBIndex index where
27− -- | Retrieves the number of records matching the key range in query.
28− count
29− :: forall e
30− . index
31− -> Maybe KeyRange
32− -> Aff (idb :: IDB | e) Int
33−
34− -- | Retrieves the value of the first record matching the given key range in query.
35− -- |
36− -- | NOTE
37− -- | The coercion from `a` to any type is unsafe and might throw a runtime error if incorrect.
38− get
39− :: forall a e
40− . index
41− -> KeyRange
42− -> Aff (idb :: IDB | e) (Maybe a)
43−
44− -- | Retrieves the keys of records matching the given key range in query
45− -- | (up to the number given if given).
46− getAllKeys
47− :: forall e
48− . index
49− -> Maybe KeyRange
50− -> Maybe Int
51− -> Aff (idb :: IDB | e) (Array Key)
52−
53− -- | Retrieves the key of the first record matching the given key or key range in query.
54− getKey
55− :: forall e
56− . index
57− -> KeyRange
58− -> Aff (idb :: IDB | e) (Maybe Key)
59−
60− -- | Opens a ValueCursor over the records matching query, ordered by direction.
61− -- | If query is `Nothing`, all records in index are matched.
62− openCursor
63− :: forall e e'
64− . index
65− -> Maybe KeyRange
66− -> CursorDirection
67− -> Callbacks ValueCursor e'
68− -> Aff (idb :: IDB | e) Unit
69−
70− -- | Opens a KeyCursor over the records matching query, ordered by direction.
71− -- | If query is `Nothing`, all records in index are matched.
72− openKeyCursor
73− :: forall e e'
74− . index
75− -> Maybe KeyRange
76− -> CursorDirection
77− -> Callbacks KeyCursor e'
78− -> Aff (idb :: IDB | e) Unit
79−
80−
81−-- | Flags to set on the index.
82−-- |
83−-- | An index has a `unique` flag. When this flag is set, the index enforces that no
84−-- | two records in the index has the same key. If a record in the index’s referenced
85−-- | object store is attempted to be inserted or modified such that evaluating the index’s
86−-- | key path on the records new value yields a result which already exists in the index,
87−-- | then the attempted modification to the object store fails.
88−-- |
89−-- | An index has a `multiEntry` flag. This flag affects how the index behaves when the
90−-- | result of evaluating the index’s key path yields an array key. If the `multiEntry` flag
91−-- | is unset, then a single record whose key is an array key is added to the index.
92−-- | If the `multiEntry` flag is true, then the one record is added to the index for each
93−-- | of the subkeys.
94−type IDBIndexParameters =
95− { unique :: Boolean
96− , multiEntry :: Boolean
97− }
98−
99−
100−-- | Callbacks to manipulate a cursor from an Open*Cursor call
101−type Callbacks cursor e =
102− { onSuccess :: cursor -> Eff ( | e) Unit
103− , onError :: Error -> Eff ( | e) Unit
104− , onComplete :: Eff ( | e) Unit
105− }
106−
107−
108−defaultParameters :: IDBIndexParameters
109−defaultParameters =
110− { unique : false
111− , multiEntry : false
112− }
113−
114−
115−--------------------
116−-- ATTRIBUTES
117−--
118−-- | Returns the key path of the index.
119−keyPath
120− :: Index
121− -> KeyPath
122−keyPath =
123− _keyPath
124−
125−
126−-- | Returns true if the index's multiEntry flag is set.
127−multiEntry
128− :: Index
129− -> Boolean
130−multiEntry =
131− _multiEntry
132−
133−
134−-- | Returns the name of the index.
135−name
136− :: Index
137− -> String
138−name =
139− _name
140−
141−
142−-- | Returns the IDBObjectStore the index belongs to.
143−objectStore
144− :: Index
145− -> ObjectStore
146−objectStore =
147− _objectStore
148−
149−
150−-- | Returns true if the index's unique flag is set.
151−unique
152− :: Index
153− -> Boolean
154−unique =
155− _unique
156−
157−
158−--------------------
159−-- INSTANCES
160−--
161−instance idbIndexIndex :: IDBIndex Index where
162− count index range =
163− Fn.runFn2 _count index (toNullable range)
164−
165− get index range =
166− (toMaybe >>> map unsafeFromForeign) <$> Fn.runFn2 _get index range
167−
168− getAllKeys index range count =
169− Fn.runFn3 _getAllKeys index (toNullable range) (toNullable count)
170−
171− getKey index range =
172− toMaybe <$> Fn.runFn2 _getKey index range
173−
174− openCursor index range dir cb =
175− Fn.runFn4 _openCursor index (toNullable range) (show dir) cb
176−
177− openKeyCursor index range dir cb =
178− Fn.runFn4 _openKeyCursor index (toNullable range) (show dir) cb
179−
180−
181−instance idbIndexObjectStore :: IDBIndex ObjectStore where
182− count store range =
183− Fn.runFn2 _count store (toNullable range)
184−
185− get store range =
186− (toMaybe >>> map unsafeFromForeign) <$> Fn.runFn2 _get store range
187−
188− getAllKeys store range count =
189− Fn.runFn3 _getAllKeys store (toNullable range) (toNullable count)
190−
191− getKey store range =
192− toMaybe <$> Fn.runFn2 _getKey store range
193−
194− openCursor store range dir cb =
195− Fn.runFn4 _openCursor store (toNullable range) (show dir) cb
196−
197− openKeyCursor store range dir cb =
198− Fn.runFn4 _openKeyCursor store (toNullable range) (show dir) cb
199−
200−
201−--------------------
202−-- FFI
203−--
204−foreign import _keyPath
205− :: Index
206− -> Array String
207−
208−
209−foreign import _multiEntry
210− :: Index
211− -> Boolean
212−
213−
214−foreign import _name
215− :: Index
216− -> String
217−
218−
219−foreign import _objectStore
220− :: Index
221− -> ObjectStore
222−
223−
224−foreign import _unique
225− :: Index
226− -> Boolean
227−
228−
229−foreign import _count
230− :: forall index e
231− . Fn2 index (Nullable KeyRange) (Aff (idb :: IDB | e) Int)
232−
233−
234−foreign import _get
235− :: forall index e
236− . Fn2 index KeyRange (Aff (idb :: IDB | e) (Nullable Foreign))
237−
238−
239−foreign import _getAllKeys
240− :: forall index e
241− . Fn3 index (Nullable KeyRange) (Nullable Int) (Aff (idb :: IDB | e) (Array Key))
242−
243−
244−foreign import _getKey
245− :: forall index e
246− . Fn2 index KeyRange (Aff (idb :: IDB | e) (Nullable Key))
247−
248−
249−foreign import _openCursor
250− :: forall index e e'
251− . Fn4 index (Nullable KeyRange) String (Callbacks ValueCursor e') (Aff (idb :: IDB | e) Unit)
252−
253−
254−foreign import _openKeyCursor
255− :: forall index e e'
256− . Fn4 index (Nullable KeyRange) String (Callbacks KeyCursor e') (Aff (idb :: IDB | e) Unit)
src/Database/IndexedDB/IDBKey.purs+8−1
@@ -5,4 +5,11 @@ module Database.IndexedDB.IDBKey
55 ( module Database.IndexedDB.IDBKey.Internal
66 ) where
77
8−import Database.IndexedDB.IDBKey.Internal(class IDBKey, Key, none, 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+96−114
@@ -2,44 +2,37 @@
22-- |
33-- | NOTE: Binary keys aren't supported yet.
44module Database.IndexedDB.IDBKey.Internal
5− ( Key(..)
6− , none
7− , class IDBKey, toKey , fromKey , unsafeFromKey
8− , extractForeign
9− ) where
5+ ( Key
6+ , class IDBKey
7+ , toKey
8+ , fromKey
9+ , unsafeFromKey
10+ , none
11+ ) where
1012
1113import Prelude
1214
13−import Control.Alt ((<|>))
14−import Control.Monad.Except (ExceptT(..), runExceptT)
15−import Data.Date as Date
16−import Data.DateTime (DateTime(..), Time(..))
17−import Data.Enum (fromEnum, toEnum)
18−import Data.Foreign as Foreign
19−import Data.Foreign (Foreign, F)
20−import Data.Function.Uncurried as Fn
21−import Data.Function.Uncurried (Fn2, Fn4, Fn7)
22−import Data.List.NonEmpty (NonEmptyList(..))
23−import Data.List.Types (List(..))
24−import Data.NonEmpty (NonEmpty(..))
25−import Data.Either (Either(..), either, isRight)
26−import Data.Maybe (Maybe(..))
27−import Data.Identity (Identity(..))
28−import Data.Nullable (Nullable, toNullable)
29−import Data.Time as Time
30−import Data.Traversable (traverse)
31−
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)
3233
3334newtype Key = Key Foreign
3435
35−
36−extractForeign
37− :: Key
38− -> Foreign
39−extractForeign (Key f) =
40− f
41−
42−
4336--------------------
4437-- INTERFACES
4538--
@@ -47,94 +40,90 @@ extractForeign (Key f) =
4740-- | to a known type (e.g if you only strings as keys, or perfectly knows the
4841-- | type of a given key).
4942class IDBKey a where
50− toKey :: a -> Key
51− fromKey :: Key -> F a
43+ toKey :: a -> Key
44+ fromKey :: Key -> F a
5245 unsafeFromKey :: Key -> a
5346
54−
5547none :: Maybe Key
5648none =
5749 Nothing
5850
59−
6051--------------------
6152-- INSTANCES
6253--
6354instance eqKey :: Eq Key where
6455 eq a b = (runExceptT >>> runIdentity >>> isRight) $
65− eq <$> ((fromKey a) :: F Int) <*> fromKey b
66− <|>
67− eq <$> ((fromKey a) :: F Number) <*> fromKey b
68− <|>
69− eq <$> ((fromKey a) :: F String) <*> fromKey b
70− <|>
71− eq <$> ((fromKey a) :: F DateTime) <*> fromKey b
72− <|>
73− 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
7465 where
75− runIdentity :: forall a. Identity a -> a
76− runIdentity (Identity x) = x
77−
66+ runIdentity :: forall a. Identity a -> a
67+ runIdentity (Identity x) = x
7868
7969instance ordKey :: Ord Key where
80− compare a b = (runExceptT >>> runIdentity >>> either (const LT) id) $
81− compare <$> ((fromKey a) :: F Int) <*> fromKey b
82− <|>
83− compare <$> ((fromKey a) :: F Number) <*> fromKey b
84− <|>
85− compare <$> ((fromKey a) :: F String) <*> fromKey b
86− <|>
87− compare <$> ((fromKey a) :: F DateTime) <*> fromKey b
88− <|>
89− 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
9080 where
91− runIdentity :: forall a. Identity a -> a
92− runIdentity (Identity x) = x
93−
81+ runIdentity :: forall a. Identity a -> a
82+ runIdentity (Identity x) = x
9483
9584instance showKey :: Show Key where
9685 show a = (runExceptT >>> format) $
97− (show <$> (fromKey a :: F Int))
98− <|>
99− (show <$> (fromKey a :: F Number))
100− <|>
101− (show <$> (fromKey a :: F String))
102− <|>
103− (show <$> (fromKey a :: F DateTime))
104− <|>
105− (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)))
10695 where
107− format :: forall a. Identity (Either a String) -> String
108− format (Identity x) =
109− either (const "(Key)") (\s -> "(Key " <> s <> ")") x
110−
96+ format :: forall a. Identity (Either a String) -> String
97+ format (Identity x) =
98+ either (const "(Key)") (\s -> "(Key " <> s <> ")") x
11199
112100instance idbKeyKey :: IDBKey Key where
113− toKey = id
114− fromKey = pure
115− unsafeFromKey = id
101+ toKey = identity
102+ fromKey = pure
103+ unsafeFromKey = identity
116104
105+instance idbKeyForeign :: IDBKey Foreign where
106+ toKey = Key
107+ fromKey (Key f) = pure f
108+ unsafeFromKey (Key f) = f
117109
118110instance idbKeyInt :: IDBKey Int where
119− toKey = Foreign.toForeign >>> Key
120− fromKey (Key f) = Foreign.readInt f
111+ toKey = Foreign.unsafeToForeign >>> Key
112+ fromKey (Key f) = Foreign.readInt f
121113 unsafeFromKey (Key f) = Foreign.unsafeFromForeign f
122114
123−
124115instance idbKeyNumber :: IDBKey Number where
125− toKey = Foreign.toForeign >>> Key
126− fromKey (Key f) = Foreign.readNumber f
116+ toKey = Foreign.unsafeToForeign >>> Key
117+ fromKey (Key f) = Foreign.readNumber f
127118 unsafeFromKey (Key f) = Foreign.unsafeFromForeign f
128119
129−
130120instance idbKeyString :: IDBKey String where
131− toKey = Foreign.toForeign >>> Key
132− fromKey (Key f) = Foreign.readString f
121+ toKey = Foreign.unsafeToForeign >>> Key
122+ fromKey (Key f) = Foreign.readString f
133123 unsafeFromKey (Key f) = Foreign.unsafeFromForeign f
134124
135−
136125instance idbKeyDate :: IDBKey DateTime where
137− toKey (DateTime d t) = Key $ Fn.runFn7 _dateTimeToForeign
126+ toKey (DateTime d t) = Key $ Fn.runFn7 _dateTimeToForeign
138127 (fromEnum $ Date.year d)
139128 (fromEnum $ Date.month d)
140129 (fromEnum $ Date.day d)
@@ -142,58 +131,51 @@ instance idbKeyDate :: IDBKey DateTime where
142131 (fromEnum $ Time.minute t)
143132 (fromEnum $ Time.second t)
144133 (fromEnum $ Time.millisecond t)
145− fromKey (Key f) = Fn.runFn4 _readDateTime dateTime dateTimeF dateTimeE f
134+ fromKey (Key f) = Fn.runFn4 _readDateTime dateTime dateTimeF dateTimeE f
146135 unsafeFromKey (Key f) = Fn.runFn2 _unsafeReadDateTime dateTime f
147136
148−
149137instance idbKeyArray :: IDBKey a => IDBKey (Array a) where
150− toKey = Foreign.toForeign >>> Key
151− fromKey (Key f) = Foreign.readArray f >>= traverse (Key >>> fromKey)
138+ toKey = Foreign.unsafeToForeign >>> Key
139+ fromKey (Key f) = Foreign.readArray f >>= traverse (Key >>> fromKey)
152140 unsafeFromKey (Key f) = map unsafeFromKey (Foreign.unsafeFromForeign f)
153141
154−
155142-- FFI constructor to build a DateTime from years, months, days, hours, minutes, seconds and millis
156143dateTime
157− :: Int -- ^ years
158− -> Int -- ^ months
159− -> Int -- ^ days
160− -> Int -- ^ hours
161− -> Int -- ^ minutes
162− -> Int -- ^ seconds
163− -> Int -- ^ milliseconds
164− -> Nullable 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
165152dateTime y m d h mi s ms =
166153 toNullable $ DateTime
167− <$> (Date.canonicalDate <$> toEnum y <*> toEnum m <*> toEnum d)
168− <*> (Time <$> toEnum h <*> toEnum mi <*> toEnum s <*> toEnum ms)
169−
154+ <$> (Date.canonicalDate <$> toEnum y <*> toEnum m <*> toEnum d)
155+ <*> (Time <$> toEnum h <*> toEnum mi <*> toEnum s <*> toEnum ms)
170156
171157-- FFI constructor to convert a JS `Date` into a successful `F DateTime`
172158dateTimeF
173− :: DateTime
174− -> F DateTime
159+ :: DateTime
160+ -> F DateTime
175161dateTimeF =
176162 Right >>> Identity >>> ExceptT
177163
178−
179164-- FFI constructor to convert a string into an errored `F DateTime`
180165dateTimeE
181− :: String
182− -> F DateTime
166+ :: String
167+ -> F DateTime
183168dateTimeE =
184169 Foreign.TypeMismatch "Date" >>> flip NonEmpty Nil >>> NonEmptyList >>> Left >>> Identity >>> ExceptT
185170
186−
187171--------------------
188172-- FFI
189173--
190174foreign import _dateTimeToForeign
191− :: Fn7 Int Int Int Int Int Int Int Foreign
192−
175+ :: Fn7 Int Int Int Int Int Int Int Foreign
193176
194177foreign import _readDateTime
195− :: Fn4 (Int -> Int -> Int -> Int -> Int -> Int -> Int -> Nullable DateTime) (DateTime -> F DateTime) (String -> F DateTime) Foreign (F DateTime)
196−
178+ :: Fn4 (Int -> Int -> Int -> Int -> Int -> Int -> Int -> Nullable DateTime) (DateTime -> F DateTime) (String -> F DateTime) Foreign (F DateTime)
197179
198180foreign import _unsafeReadDateTime
199− :: Fn2 (Int -> Int -> Int -> Int -> Int -> Int -> Int -> Nullable DateTime) Foreign DateTime
181+ :: Fn2 (Int -> Int -> Int -> Int -> Int -> Int -> Int -> Nullable DateTime) Foreign DateTime
src/Database/IndexedDB/IDBKeyRange.js+9−9
@@ -1,4 +1,4 @@
1−exports._bound = function _bound(lower, upper, lowerOpen, upperOpen) {
1+export function _bound(lower, upper, lowerOpen, upperOpen) {
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+91−75
@@ -1,71 +1,76 @@
11-- | A key has an associated type which is one of: number, date, string, binary, or array.
22module Database.IndexedDB.IDBKeyRange
3− ( class IDBKeyRange, includes
3+ -- * Types
4+ ( Open
5+
6+ -- * Constructors
47 , only
58 , lowerBound
69 , upperBound
710 , bound
11+
12+ -- * Interface
13+ , includes
14+
15+ -- * Attributes
816 , lower
917 , upper
1018 , lowerOpen
1119 , upperOpen
1220 ) where
1321
14−import Prelude
15−
16−import Data.Foreign (Foreign)
17−import Data.Function.Uncurried as Fn
18−import Data.Function.Uncurried (Fn2, Fn4)
19−import Data.Maybe (Maybe)
20−import Data.Nullable (Nullable, toMaybe)
22+import Prelude (($), (>>>), map)
2123
22−import Database.IndexedDB.Core (KeyRange)
23−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)
2429
30+import Database.IndexedDB.Core (class IDBKeyRange, KeyRange)
31+import Database.IndexedDB.IDBKey.Internal (class IDBKey, Key, toKey, unsafeFromKey)
2532
2633--------------------
27−-- INTERFACES
34+-- TYPES
2835--
29−-- | The IDBKeyRange interface represents a key range.
30−class IDBKeyRange range where
31− -- | Returns true if key is included in the range, and false otherwise.
32− includes :: range -> Key -> Boolean
33−
3436
3537-- | Type alias for open
3638type Open = Boolean
3739
40+--------------------
41+-- CONSTRUCTORS
42+--
3843
3944-- | Returns a new IDBKeyRange spanning only key.
4045only
41− :: forall a. (IDBKey a)
42− => a
43− -> KeyRange
46+ :: forall a
47+ . (IDBKey a)
48+ => a
49+ -> KeyRange
4450only key =
45− _only (extractForeign $ toKey key)
46−
51+ _only (unsafeFromKey $ toKey key)
4752
4853-- | Returns a new IDBKeyRange starting at key with no upper bound.
4954-- | If `Open` is `true`, key is not included in the range.
5055lowerBound
51− :: forall a. (IDBKey a)
52− => a
53− -> Open
54− -> KeyRange
56+ :: forall a
57+ . (IDBKey a)
58+ => a
59+ -> Open
60+ -> KeyRange
5561lowerBound key open =
56− Fn.runFn2 _lowerBound (extractForeign $ toKey key) open
57−
62+ Fn.runFn2 _lowerBound (unsafeFromKey $ toKey key) open
5863
5964-- | Returns a new IDBKeyRange with no lower bound and ending at key.
6065-- | If `Open` is `true`, key is not included in the range.
6166upperBound
62− :: forall a. (IDBKey a)
63− => a
64− -> Open
65− -> KeyRange
67+ :: forall a
68+ . (IDBKey a)
69+ => a
70+ -> Open
71+ -> KeyRange
6672upperBound key open =
67− Fn.runFn2 _upperBound (extractForeign $ toKey key) open
68−
73+ Fn.runFn2 _upperBound (unsafeFromKey $ toKey key) open
6974
7075-- | Returns a new IDBKeyRange spanning from `lower` to `upper`.
7176-- | If `lowerOpen` is `true`, `lower` is not included in the range.
@@ -73,82 +78,93 @@ upperBound key open =
7378-- |
7479-- | It throws a `DataError` if the bound is invalid.
7580bound
76− :: forall a. (IDBKey a)
77− => { lower :: a, upper :: a, lowerOpen :: Boolean, upperOpen :: Boolean }
78− -> Maybe KeyRange
81+ :: forall key
82+ . (IDBKey key)
83+ => { lower :: key, upper :: key, lowerOpen :: Boolean, upperOpen :: Boolean }
84+ -> Maybe KeyRange
7985bound { lower: key1, upper: key2, lowerOpen: open1, upperOpen: open2 } =
8086 toMaybe
81− $ Fn.runFn4 _bound (extractForeign $ toKey key1) (extractForeign $ toKey key2) open1 open2
87+ $ Fn.runFn4 _bound (unsafeFromKey $ toKey key1) (unsafeFromKey $ toKey key2) open1 open2
8288
89+--------------------
90+-- INTERFACE
91+--
92+
93+-- | Returns true if key is included in the range, and false otherwise.
94+includes
95+ :: forall key range
96+ . (IDBKey key)
97+ => (IDBKeyRange range)
98+ => range
99+ -> key
100+ -> Boolean
101+includes range =
102+ toKey >>> unsafeFromKey >>> Fn.runFn2 _includes range
83103
84104--------------------
85105-- ATTRIBUTES
86106--
87107-- | Returns lower bound if any.
88108lower
89− :: KeyRange
90− -> Maybe Key
109+ :: KeyRange
110+ -> Maybe Key
91111lower =
92− _lower >>> toMaybe >>> map Key
93−
112+ _lower >>> toMaybe >>> map toKey
94113
95114-- | Returns upper bound if any.
96115upper
97− :: KeyRange
98− -> Maybe Key
116+ :: KeyRange
117+ -> Maybe Key
99118upper =
100− _upper >>> toMaybe >>> map Key
101−
119+ _upper >>> toMaybe >>> map toKey
102120
103121-- | Returns true if the lower open flag is set, false otherwise.
104122lowerOpen
105− :: KeyRange
106− -> Boolean
123+ :: KeyRange
124+ -> Boolean
107125lowerOpen =
108126 _lowerOpen
109127
110−
111128-- | Returns true if the upper open flag is set, false otherwise.
112129upperOpen
113− :: KeyRange
114− -> Boolean
130+ :: KeyRange
131+ -> Boolean
115132upperOpen =
116133 _upperOpen
117134
118−
119−--------------------
120−-- INSTANCES
121−--
122−instance idbKeyRangeKeyRange :: IDBKeyRange KeyRange where
123− includes range =
124− extractForeign >>> Fn.runFn2 _includes range
125−
126−
127135--------------------
128136-- FFI
129137--
130−foreign import _bound :: Fn4 Foreign Foreign Boolean Boolean (Nullable KeyRange)
131−
132−
133−foreign import _includes :: forall range. Fn2 range Foreign Boolean
134−
135−
136−foreign import _lower :: KeyRange -> Nullable Foreign
137−
138−
139−foreign import _lowerBound :: Fn2 Foreign Boolean KeyRange
140−
141138
142−foreign import _lowerOpen :: KeyRange -> Boolean
139+foreign import _only
140+ :: Foreign
141+ -> KeyRange
143142
143+foreign import _lowerBound
144+ :: Fn2 Foreign Boolean KeyRange
144145
145−foreign import _only :: Foreign -> KeyRange
146+foreign import _upperBound
147+ :: Fn2 Foreign Boolean KeyRange
146148
149+foreign import _bound
150+ :: Fn4 Foreign Foreign Boolean Boolean (Nullable KeyRange)
147151
148−foreign import _upper :: KeyRange -> Nullable Foreign
152+foreign import _includes
153+ :: forall range
154+ . Fn2 range Foreign Boolean
149155
156+foreign import _lower
157+ :: KeyRange
158+ -> Nullable Foreign
150159
151−foreign import _upperBound :: Fn2 Foreign Boolean KeyRange
160+foreign import _upper
161+ :: KeyRange
162+ -> Nullable Foreign
152163
164+foreign import _lowerOpen
165+ :: KeyRange
166+ -> Boolean
153167
154−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+160−156
@@ -1,124 +1,170 @@
11-- | An object store is the primary storage mechanism for storing data in a database.
22module Database.IndexedDB.IDBObjectStore
3− ( class IDBObjectStore, add, clear, createIndex, delete, deleteIndex, index, put
4− , module Database.IndexedDB.IDBIndex.Internal
5− , IDBObjectStoreParameters
6− , 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
718 , autoIncrement
819 , indexNames
920 , keyPath
1021 , name
1122 , transaction
12− , defaultParameters
13− ) where
14−
15−import Prelude (Unit, ($), (<$>), (>>>))
1623
17−import Control.Monad.Aff (Aff)
18−import Data.Foreign (Foreign)
19−import Data.Function.Uncurried as Fn
20−import Data.Function.Uncurried (Fn2, Fn3, Fn4)
21−import Data.Maybe (Maybe)
22−import Data.Nullable (Nullable, toNullable)
24+ -- * Re-Exports
25+ , module Database.IndexedDB.IDBIndex
26+ ) where
2327
24−import Database.IndexedDB.Core (IDB, Index, KeyRange, KeyPath, ObjectStore, Transaction)
25−import Database.IndexedDB.IDBIndex.Internal (class IDBIndex, IDBIndexParameters, count, get, getAllKeys, getKey, openCursor, openKeyCursor)
26−import Database.IndexedDB.IDBKey.Internal (class IDBKey, Key(Key), extractForeign, toKey)
28+import Database.IndexedDB.Core
2729
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, ($), (<$>), (>>>), (<<<))
2840
2941--------------------
30−-- INTERFACES
42+-- TYPES
3143--
32−-- | The IDBObjectStore interface represents an object store handle.
33−class IDBObjectStore store where
34− -- | Adds or updates a record in store with the given value and key.
35− -- |
36− -- | If the store uses in-line keys and key is specified a "DataError" DOMException
37− -- | will be thrown.
38− -- |
39− -- | If add() is used, and if a record with the key already exists the request will fail,
40− -- | with a "ConstraintError" DOMException.
41− add
42− :: forall v k e. (IDBKey k)
43− => store
44− -> v
45− -> Maybe k
46− -> Aff (idb :: IDB | e) Key
47−
48− -- | Deletes all records in store.
49− clear
50− :: forall e
51− . store
52− -> Aff (idb :: IDB | e) Unit
53−
54− -- | Creates a new index in store with the given name, keyPath and options and
55− -- | returns a new IDBIndex. If the keyPath and options define constraints that
56− -- | cannot be satisfied with the data already in store the upgrade transaction
57− -- | will abort with a "ConstraintError" DOMException.
58− -- |
59− -- | Throws an "InvalidStateError" DOMException if not called within an upgrade transaction.
60− createIndex
61− :: forall e
62− . store
63− -> IndexName
64− -> KeyPath
65− -> IDBIndexParameters
66− -> Aff (idb :: IDB | e) Index
67−
68− -- | Deletes records in store with the given key or in the given key range in query.
69− delete
70− :: forall e
71− . store
72− -> KeyRange
73− -> Aff (idb :: IDB | e) Unit
74−
75− -- | Deletes the index in store with the given name.
76− -- |
77− -- | Throws an "InvalidStateError" DOMException if not called within an upgrade transaction.
78− deleteIndex
79− :: forall e
80− . store
81− -> IndexName
82− -> Aff (idb :: IDB | e) Unit
83−
84− -- | Returns an IDBIndex for the index named name in store.
85− index
86− :: forall e
87− . store
88− -> IndexName
89− -> Aff (idb :: IDB | e) Index
90−
91− -- | Adds or updates a record in store with the given value and key.
92− -- |
93− -- | If the store uses in-line keys and key is specified a "DataError" DOMException
94− -- | will be thrown.
95− -- |
96− -- | If put() is used, any existing record with the key will be replaced.
97− put
98− :: forall v k e. (IDBKey k)
99− => store
100− -> v
101− -> Maybe k
102− -> Aff (idb :: IDB | e) Key
103−
10444
10545-- | Type alias for IndexName
10646type IndexName = String
10747
108−
109−-- | Options provided when creating an object store.
110−type IDBObjectStoreParameters =
111− { keyPath :: KeyPath
112− , autoIncrement :: Boolean
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
11364 }
11465
115−
116−defaultParameters :: IDBObjectStoreParameters
66+defaultParameters :: IndexParameters
11767defaultParameters =
118− { keyPath : []
119− , autoIncrement : false
68+ { unique: false
69+ , multiEntry: false
12070 }
12171
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)
122168
123169--------------------
124170-- ATTRIBUTES
@@ -130,7 +176,6 @@ autoIncrement
130176autoIncrement =
131177 _autoIncrement
132178
133−
134179--| Returns a list of the names of indexes in the store.
135180indexNames
136181 :: ObjectStore
@@ -138,7 +183,6 @@ indexNames
138183indexNames =
139184 _indexNames
140185
141−
142186-- | Returns the key path of the store, or empty array if none
143187keyPath
144188 :: ObjectStore
@@ -146,7 +190,6 @@ keyPath
146190keyPath =
147191 _keyPath
148192
149−
150193-- | Returns the name of the store.
151194name
152195 :: ObjectStore
@@ -154,7 +197,6 @@ name
154197name =
155198 _name
156199
157−
158200-- | Returns the associated transaction.
159201transaction
160202 :: ObjectStore
@@ -162,91 +204,53 @@ transaction
162204transaction =
163205 _transaction
164206
165−
166−--------------------
167−-- INSTANCES
168−--
169−instance idbObjectStoreObjectStore :: IDBObjectStore ObjectStore where
170− add store value key =
171− Key <$> Fn.runFn3 _add store value (toNullable $ (toKey >>> extractForeign) <$> key)
172−
173− clear =
174− _clear
175−
176− createIndex store name' path params =
177− Fn.runFn4 _createIndex store name' path params
178−
179− delete store range =
180− Fn.runFn2 _delete store range
181−
182− deleteIndex store name' =
183− Fn.runFn2 _deleteIndex store name'
184−
185− index store name' =
186− Fn.runFn2 _index store name'
187−
188− put store value key =
189− Key <$> Fn.runFn3 _put store value (toNullable $ (toKey >>> extractForeign) <$> key)
190−
191−
192207--------------------
193208-- FFI
194209--
195210foreign import _add
196− :: forall value e
197− . Fn3 ObjectStore value (Nullable Foreign) (Aff (idb :: IDB | e) Foreign)
198−
211+ :: forall val store
212+ . Fn3 store val (Nullable Foreign) (EffectFnAff Foreign)
199213
200214foreign import _autoIncrement
201215 :: ObjectStore
202216 -> Boolean
203217
204−
205218foreign import _clear
206− :: forall e
207− . ObjectStore
208− -> Aff (idb :: IDB | e) Unit
209−
219+ :: forall store
220+ . store
221+ -> EffectFnAff Unit
210222
211223foreign import _createIndex
212− :: forall e
213− . Fn4 ObjectStore String (Array String) { unique :: Boolean, multiEntry :: Boolean } (Aff (idb :: IDB | e) Index)
214−
224+ :: forall store
225+ . Fn4 store String (Array String) { unique :: Boolean, multiEntry :: Boolean } (EffectFnAff Index)
215226
216227foreign import _delete
217− :: forall e
218− . Fn2 ObjectStore KeyRange (Aff (idb :: IDB | e) Unit)
219−
228+ :: forall store
229+ . Fn2 store KeyRange (EffectFnAff Unit)
220230
221231foreign import _deleteIndex
222− :: forall e
223− . Fn2 ObjectStore String (Aff (idb :: IDB | e) Unit)
224−
232+ :: forall store
233+ . Fn2 store String (EffectFnAff Unit)
225234
226235foreign import _index
227− :: forall e
228− . Fn2 ObjectStore String (Aff (idb :: IDB | e) Index)
229−
236+ :: forall store
237+ . Fn2 store String (EffectFnAff Index)
230238
231239foreign import _indexNames
232240 :: ObjectStore
233241 -> Array String
234242
235−
236243foreign import _keyPath
237244 :: ObjectStore
238245 -> Array String
239246
240−
241247foreign import _name
242248 :: ObjectStore
243249 -> String
244250
245−
246251foreign import _put
247− :: forall value e
248− . Fn3 ObjectStore value (Nullable Foreign) (Aff (idb :: IDB | e) Foreign)
249−
252+ :: forall val store
253+ . Fn3 store val (Nullable Foreign) (EffectFnAff Foreign)
250254
251255foreign import _transaction
252256 :: ObjectStore
src/Database/IndexedDB/IDBTransaction.js+38−18
@@ -1,25 +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._db = function _db(tx) {
16+export function _db(tx) {
1317 return tx.db;
1418};
1519
16−exports._error = function _error(tx) {
20+export function _error(tx) {
1721 return tx.error == null
1822 ? null
19− : new Error(tx.error.name);
23+ : tx.error;
2024};
2125
22−exports._mode = function _mode(ReadOnly, ReadWrite, VersionChange, tx) {
26+export function _mode(ReadOnly, ReadWrite, VersionChange, tx) {
2327 if (tx.mode === 'readwrite') {
2428 return ReadWrite;
2529 } else if (tx.mode === 'versionchange') {
@@ -29,44 +33,60 @@ exports._mode = function _mode(ReadOnly, ReadWrite, VersionChange, tx) {
2933 return ReadOnly;
3034};
3135
32−exports._objectStore = function _objectStore(tx, name) {
33− return function aff(success, error) {
36+export function _objectStore(tx, name) {
37+ return function aff(error, success) {
3438 try {
3539 const store = tx.objectStore(name);
3640 success(store);
3741 } catch (e) {
38− error(new Error(e.name));
42+ error(e);
3943 }
44+
45+ return function canceler(_, cancelerError) {
46+ cancelerError(new Error("Can't cancel IDB Effects"));
47+ };
4048 };
4149};
4250
43−exports._objectStoreNames = function _objectStoreNames(tx) {
51+export function _objectStoreNames(tx) {
4452 return tx.objectStoreNames;
4553};
4654
47−exports._onAbort = function _onAbort(tx, f) {
48− return function aff(success) {
55+export function _onAbort(tx, f) {
56+ return function aff(error, success) {
4957 tx.onabort = function onabort() {
5058 f();
5159 };
5260 success();
61+
62+ return function canceler(_, cancelerError) {
63+ cancelerError(new Error("Can't cancel IDB Effects"));
64+ };
5365 };
5466};
5567
56−exports._onComplete = function _onComplete(tx, f) {
57− return function aff(success) {
68+export function _onComplete(tx, f) {
69+ return function aff(error, success) {
5870 tx.oncomplete = function oncomplete() {
5971 f();
6072 };
6173 success();
74+
75+ return function canceler(_, cancelerError) {
76+ cancelerError(new Error("Can't cancel IDB Effects"));
77+ };
6278 };
6379};
6480
65−exports._onError = function _onError(tx, f) {
66− return function aff(success) {
81+export function _onError(tx, f) {
82+ return function aff(error, success) {
6783 tx.onerror = function onerror(e) {
68− f(new Error(e.target.error.name))();
84+ f(e.target.error)();
6985 };
7086 success();
87+
88+ return function canceler(_, cancelerError) {
89+ cancelerError(new Error("Can't cancel IDB Effects"));
90+ };
7191 };
7292};
src/Database/IndexedDB/IDBTransaction.purs+66−83
@@ -1,58 +1,66 @@
1−-- | A Transaction is used to interact with the data in a database.
2−-- | Whenever data is read or written to the database it is done by using a transaction.
1+-- | An object store is the primary storage mechanism for storing data in a database.
32module Database.IndexedDB.IDBTransaction
4− (class IDBTransaction, abort, objectStore
3+ -- * Interface
4+ ( abort
5+ , objectStore
6+
7+ -- * Attributes
58 , error
69 , mode
10+
11+ -- * Event Handlers
712 , onAbort
813 , onComplete
914 , onError
1015 ) where
1116
12−import Prelude (Unit, (>>>))
13−
14−import Control.Monad.Aff (Aff)
15−import Control.Monad.Eff (Eff)
16−import Control.Monad.Eff.Exception (Error)
17−import Data.Function.Uncurried as Fn
18−import Data.Function.Uncurried (Fn2, Fn4)
19−import Data.Maybe (Maybe)
20−import Data.Nullable (Nullable, toMaybe)
21−
22−import Database.IndexedDB.Core (IDB, Database, ObjectStore, Transaction, TransactionMode(..))
17+import Database.IndexedDB.Core
2318
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, ($), (<<<), (>>>))
2428
2529--------------------
2630-- INTERFACES
2731--
28−-- | The IDBtransaction interface.
29−class IDBTransaction tx where
30− -- | Aborts the transaction. All pending requests will fail with a "AbortError"
31− -- | DOMException and all changes made to the database will be reverted.
32− abort
33− :: forall e
34− . tx
35− -> Aff (idb :: IDB | e) Unit
36−
37− -- | Returns an IDBObjectStore in the transaction's scope.
38− objectStore
39− :: forall e
40− . tx
41− -> String
42− -> Aff (idb :: IDB | e) ObjectStore
4332
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
4452
4553--------------------
4654-- ATTRIBUTES
4755--
48−-- | Returns the transaction’s connection.
56+
57+--- | Returns the transaction’s connection.
4958db
5059 :: Transaction
5160 -> Database
5261db =
5362 _db
5463
55−
5664-- | If the transaction was aborted, returns the error (a DOMException) providing the reason.
5765error
5866 :: Transaction
@@ -68,7 +76,6 @@ mode
6876mode =
6977 Fn.runFn4 _mode ReadOnly ReadWrite VersionChange
7078
71−
7279-- | Returns a list of the names of object stores in the transaction’s scope.
7380-- | For an upgrade transaction this is all object stores in the database.
7481objectStoreNames
@@ -80,91 +87,67 @@ objectStoreNames =
8087--------------------
8188-- EVENT HANDLERS
8289--
90+
8391-- | Event handler for the `abort` event.
8492onAbort
85− :: forall e e'
86− . Transaction
87− -> Eff ( | e') Unit
88− -> Aff (idb :: IDB | e) Unit
89−onAbort db f =
90− Fn.runFn2 _onAbort db f
91−
93+ :: Transaction
94+ -> Effect Unit
95+ -> Aff Unit
96+onAbort db' f =
97+ fromEffectFnAff $ Fn.runFn2 _onAbort db' f
9298
9399-- | Event handler for the `complete` event.
94100onComplete
95− :: forall e e'
96− . Transaction
97− -> Eff ( | e') Unit
98− -> Aff (idb :: IDB | e) Unit
99−onComplete db f =
100− Fn.runFn2 _onComplete db f
101−
101+ :: Transaction
102+ -> Effect Unit
103+ -> Aff Unit
104+onComplete db' f =
105+ fromEffectFnAff $ Fn.runFn2 _onComplete db' f
102106
103107-- | Event handler for the `error` event.
104108onError
105− :: forall e e'
106− . Transaction
107− -> (Error -> Eff ( | e') Unit)
108− -> Aff (idb :: IDB | e) Unit
109+ :: Transaction
110+ -> (Error -> Effect Unit)
111+ -> Aff Unit
109112onError db' f =
110− Fn.runFn2 _onError db' f
111−
112−
113−--------------------
114−-- INSTANCES
115−--
116−instance idbTransactionTransaction :: IDBTransaction Transaction where
117− objectStore tx name =
118− Fn.runFn2 _objectStore tx name
119−
120− abort =
121− _abort
122−
113+ fromEffectFnAff $ Fn.runFn2 _onError db' f
123114
124115--------------------
125116-- FFI
126117--
127−foreign import _abort
128− :: forall tx e
129− . tx
130− -> Aff (idb :: IDB | e) Unit
131118
119+foreign import _abort
120+ :: forall tx
121+ . tx
122+ -> EffectFnAff Unit
132123
133124foreign import _db
134125 :: Transaction
135126 -> Database
136127
137−
138128foreign import _error
139129 :: Transaction
140130 -> (Nullable Error)
141131
142−
143132foreign import _mode
144133 :: Fn4 TransactionMode TransactionMode TransactionMode Transaction TransactionMode
145134
146−
147135foreign import _objectStoreNames
148136 :: Transaction
149137 -> Array String
150138
151−
152139foreign import _objectStore
153− :: forall tx e
154− . Fn2 tx String (Aff (idb :: IDB | e) ObjectStore)
155−
140+ :: forall tx
141+ . Fn2 tx String (EffectFnAff ObjectStore)
156142
157143foreign import _onAbort
158− :: forall tx e e'
159− . Fn2 tx (Eff ( | e') Unit) (Aff (idb :: IDB | e) Unit)
160−
144+ :: forall tx
145+ . Fn2 tx (Effect Unit) (EffectFnAff Unit)
161146
162147foreign import _onComplete
163− :: forall tx e e'
164− . Fn2 tx (Eff ( | e') Unit) (Aff (idb :: IDB | e) Unit)
165−
148+ :: forall tx
149+ . Fn2 tx (Effect Unit) (EffectFnAff Unit)
166150
167151foreign import _onError
168− :: forall tx e e'
169− . Fn2 tx (Error -> Eff ( | e') Unit) (Aff (idb :: IDB | e) Unit)
170−
152+ :: forall tx
153+ . Fn2 tx (Error -> Effect Unit) (EffectFnAff Unit)
test/Main.purs+974−904
@@ -2,995 +2,1060 @@ 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.Array (head, drop)
20−import Data.Time.Duration (Milliseconds(..))
21−import Data.Traversable (traverse)
22−import Data.Tuple (Tuple(..), uncurry)
23−import Test.Spec (describe, describeOnly, it)
24−import Test.Spec.Assertions (shouldEqual, fail)
25−import Test.Spec.Mocha (MOCHA, runMocha)
26−
27−import Database.IndexedDB.Core
28−import Database.IndexedDB.IDBKey
29−import Database.IndexedDB.IDBCursor as IDBCursor
30−import Database.IndexedDB.IDBDatabase as IDBDatabase
31−import Database.IndexedDB.IDBFactory as IDBFactory
32−import Database.IndexedDB.IDBIndex as IDBIndex
33−import Database.IndexedDB.IDBKeyRange as IDBKeyRange
34−import Database.IndexedDB.IDBObjectStore as IDBObjectStore
35−import Database.IndexedDB.IDBTransaction as IDBTransaction
36−
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)
3736
3837infixr 7 Tuple as :+:
3938
40−launchAff' :: forall a e. Aff e a -> Eff (exception :: EXCEPTION | e) Unit
41−launchAff' aff =
42− 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
4347
44−-- main :: forall eff. Eff (now :: NOW, mocha :: MOCHA, idb :: IDB, exception :: EXCEPTION, avar :: AVAR | eff) Unit
45−main = runMocha do
46− describe "IDBFactory" do
47− let
48+main :: Effect Unit
49+main = do
50+ launchAff_ $ runSpec [ consoleReporter ] $ do
51+ describe "IDBFactory" do
52+ let
4853 tearDown name version db = do
4954 IDBDatabase.close db
5055 version' <- IDBFactory.deleteDatabase name
56+ liftEffect $ log $ show version'
5157 version' `shouldEqual` version
5258
53− it "open default" do
54− let name = "db-default"
59+ it "open default" do
60+ let
61+ name = "db-default"
5562 version = 1
56− db <- IDBFactory.open name Nothing
57− { onUpgradeNeeded : Nothing
58− , onBlocked : Nothing
59− }
60− IDBDatabase.name db `shouldEqual` name
61− tearDown name version db
62−
63+ db <- IDBFactory.open name Nothing
64+ { onUpgradeNeeded: Nothing
65+ , onBlocked: Nothing
66+ }
67+ IDBDatabase.name db `shouldEqual` name
68+ tearDown name version db
6369
64− it "open specific version" do
65− let name = "db-specific"
70+ it "open specific version" do
71+ let
72+ name = "db-specific"
6673 version = 14
67− db <- IDBFactory.open name (Just version)
68− { onUpgradeNeeded : Nothing
69− , onBlocked : Nothing
70− }
71− IDBDatabase.name db `shouldEqual` name
72− tearDown name version db
73−
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
7480
75− it "open specific version -> close -> open latest" do
76− let name = "db-latest"
81+ it "open specific version -> close -> open latest" do
82+ let
83+ name = "db-latest"
7784 version = 14
78− db <- IDBFactory.open name (Just version)
79− { onUpgradeNeeded : Nothing
80− , onBlocked : Nothing
81− }
82− IDBDatabase.name db `shouldEqual` name
83− IDBDatabase.close db
84− db <- IDBFactory.open name Nothing
85− { onUpgradeNeeded : Nothing
86− , onBlocked : Nothing
87− }
88− tearDown name version db
89−
90−
91− it "open + onUpgradeNeed" do
92− 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"
93100 version = 1
94− callback var db _ = do
95− _ <- 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
96104 pure unit
97− var <- makeVar' "-"
98− db <- IDBFactory.open name Nothing
99− { onUpgradeNeeded : Just (callback var)
100− , onBlocked : Nothing
101− }
102− name <- peekVar var
103− name `shouldEqual` name
104− tearDown name version db
105−
106−
107− it "open + onBlocked" do
108− 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"
109120 version = 14
110121 callback var = do
111− _ <- launchAff $ modifyVar (const $ "db-blocked") var
122+ _ <- launchAff $ modifyAVar (const $ "db-blocked") var
112123 pure unit
113124
114− var <- makeVar' "-"
115− db01 <- IDBFactory.open name Nothing
116− { onUpgradeNeeded : Nothing
117− , onBlocked : Nothing
118− }
119− _ <- forkAff do
120− delay (Milliseconds 100.0)
121− 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
122133
123− db02 <- IDBFactory.open name (Just version)
124− { onUpgradeNeeded : Nothing
125− , onBlocked : Just (callback var)
126− }
127− name <- peekVar var
128− name `shouldEqual` name
129− tearDown name version db02
130−
131− describe "IDBKeyRange" do
132− it "only(int)" do
133− 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
134146 range = IDBKeyRange.only key
135− IDBKeyRange.includes range (toKey key) `shouldEqual` true
147+ IDBKeyRange.includes range (toKey key) `shouldEqual` true
136148
137− it "only(string)" do
138− let key = "patate"
149+ it "only(string)" do
150+ let
151+ key = "patate"
139152 range = IDBKeyRange.only key
140− IDBKeyRange.includes range (toKey key) `shouldEqual` true
153+ IDBKeyRange.includes range (toKey key) `shouldEqual` true
141154
142− it "only(float)" do
143− let key = 14.42
155+ it "only(float)" do
156+ let
157+ key = 14.42
144158 range = IDBKeyRange.only key
145− IDBKeyRange.includes range (toKey key) `shouldEqual` true
159+ IDBKeyRange.includes range (toKey key) `shouldEqual` true
146160
147− it "only(date)" do
148− let mkey = DateTime
161+ it "only(date)" do
162+ let
163+ mkey = DateTime
149164 <$> (Date.canonicalDate <$> toEnum 2017 <*> toEnum 6 <*> toEnum 23)
150165 <*> (Time <$> toEnum 17 <*> toEnum 59 <*> toEnum 34 <*> toEnum 42)
151166
152− case mkey of
153− Nothing ->
154− fail "unable to create datetime"
155− Just key -> do
156− let range = IDBKeyRange.only key
157− 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
158173
159− it "only([int])" do
160− let key = [14, 42]
174+ it "only([int])" do
175+ let
176+ key = [ 14, 42 ]
161177 range = IDBKeyRange.only key
162− IDBKeyRange.includes range (toKey key) `shouldEqual` true
178+ IDBKeyRange.includes range (toKey key) `shouldEqual` true
163179
164− it "only([string])" do
165− let key = ["patate", "autruche"]
180+ it "only([string])" do
181+ let
182+ key = [ "patate", "autruche" ]
166183 range = IDBKeyRange.only key
167− IDBKeyRange.includes range (toKey key) `shouldEqual` true
184+ IDBKeyRange.includes range (toKey key) `shouldEqual` true
168185
169− it "lowerBound(14, open)" do
170− let key = 14
171− open = true
186+ it "lowerBound(14, open)" do
187+ let
188+ key = 14
189+ open = true
172190 range = IDBKeyRange.lowerBound key open
173− IDBKeyRange.includes range (toKey (key + 1)) `shouldEqual` true
174− IDBKeyRange.includes range (toKey key) `shouldEqual` (not open)
175− IDBKeyRange.includes range (toKey (key - 1)) `shouldEqual` false
176−
177− it "lowerBound(14, close)" do
178− let key = 14
179− 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
180199 range = IDBKeyRange.lowerBound key open
181− IDBKeyRange.includes range (toKey (key + 1)) `shouldEqual` true
182− IDBKeyRange.includes range (toKey key) `shouldEqual` (not open)
183− IDBKeyRange.includes range (toKey (key - 1)) `shouldEqual` false
184−
185− it "upperBound(14, open)" do
186− let key = 14
187− 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
188208 range = IDBKeyRange.upperBound key open
189− IDBKeyRange.includes range (toKey (key + 1)) `shouldEqual` false
190− IDBKeyRange.includes range (toKey key) `shouldEqual` (not open)
191− IDBKeyRange.includes range (toKey (key - 1)) `shouldEqual` true
192−
193− it "upperBound(14, close)" do
194− let key = 14
195− 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
196217 range = IDBKeyRange.upperBound key open
197− IDBKeyRange.includes range (toKey (key + 1)) `shouldEqual` false
198− IDBKeyRange.includes range (toKey key) `shouldEqual` (not open)
199− IDBKeyRange.includes range (toKey (key - 1)) `shouldEqual` true
200−
201− it "bound(42, 14, open, open) => Nothing" do
202− let lower = 42
203− 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
204226 lowerOpen = true
205227 upperOpen = true
206− mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
207− isNothing mrange `shouldEqual` true
228+ mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
229+ isNothing mrange `shouldEqual` true
208230
209− it "bound(14, 42, open, open)" do
210− let lower = 14
211− upper = 42
231+ it "bound(14, 42, open, open)" do
232+ let
233+ lower = 14
234+ upper = 42
212235 lowerOpen = true
213236 upperOpen = true
214− mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
215− case mrange of
216− Nothing ->
217− fail "invalid range provided"
218− Just range -> do
219− IDBKeyRange.includes range (toKey (lower + 1)) `shouldEqual` true
220− IDBKeyRange.includes range (toKey (upper - 1)) `shouldEqual` true
221− IDBKeyRange.includes range (toKey (lower - 1)) `shouldEqual` false
222− IDBKeyRange.includes range (toKey (upper + 1)) `shouldEqual` false
223− IDBKeyRange.includes range (toKey lower) `shouldEqual` (not lowerOpen)
224− IDBKeyRange.includes range (toKey upper) `shouldEqual` (not upperOpen)
225−
226− it "bound(14, 42, open, close)" do
227− let lower = 14
228− 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
229253 lowerOpen = true
230254 upperOpen = false
231− mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
232− case mrange of
233− Nothing ->
234− fail "invalid range provided"
235− Just range -> do
236− IDBKeyRange.includes range (toKey (lower + 1)) `shouldEqual` true
237− IDBKeyRange.includes range (toKey (upper - 1)) `shouldEqual` true
238− IDBKeyRange.includes range (toKey (lower - 1)) `shouldEqual` false
239− IDBKeyRange.includes range (toKey (upper + 1)) `shouldEqual` false
240− IDBKeyRange.includes range (toKey lower) `shouldEqual` (not lowerOpen)
241− IDBKeyRange.includes range (toKey upper) `shouldEqual` (not upperOpen)
242−
243− it "bound(14, 42, close, open)" do
244− let lower = 14
245− 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
246271 lowerOpen = false
247272 upperOpen = true
248− mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
249− case mrange of
250− Nothing ->
251− fail "invalid range provided"
252− Just range -> do
253− IDBKeyRange.includes range (toKey (lower + 1)) `shouldEqual` true
254− IDBKeyRange.includes range (toKey (upper - 1)) `shouldEqual` true
255− IDBKeyRange.includes range (toKey (lower - 1)) `shouldEqual` false
256− IDBKeyRange.includes range (toKey (upper + 1)) `shouldEqual` false
257− IDBKeyRange.includes range (toKey lower) `shouldEqual` (not lowerOpen)
258− IDBKeyRange.includes range (toKey upper) `shouldEqual` (not upperOpen)
259−
260− it "bound(14, 42, close, close)" do
261− let lower = 14
262− 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
263289 lowerOpen = false
264290 upperOpen = false
265− mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
266− case mrange of
267− Nothing ->
268− fail "invalid range provided"
269− Just range -> do
270− IDBKeyRange.includes range (toKey (lower + 1)) `shouldEqual` true
271− IDBKeyRange.includes range (toKey (upper - 1)) `shouldEqual` true
272− IDBKeyRange.includes range (toKey (lower - 1)) `shouldEqual` false
273− IDBKeyRange.includes range (toKey (upper + 1)) `shouldEqual` false
274− IDBKeyRange.includes range (toKey lower) `shouldEqual` (not lowerOpen)
275− IDBKeyRange.includes range (toKey upper) `shouldEqual` (not upperOpen)
276−
277− it "can access attributes of a range" do
278− let range = IDBKeyRange.lowerBound 14 false
279− IDBKeyRange.lower range `shouldEqual` (Just $ toKey 14)
280− IDBKeyRange.upper range `shouldEqual` none
281− IDBKeyRange.lowerOpen range `shouldEqual` false
282− IDBKeyRange.upperOpen range `shouldEqual` true
283−
284− describe "IDBDatabase" do
285− 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
286312 tearDown db = do
287313 IDBDatabase.close db
288314 _ <- IDBFactory.deleteDatabase (IDBDatabase.name db)
289315 pure unit
290316
291317 setup storeParams = do
292− let onUpgradeNeeded var db _ = launchAff' do
293− store <- IDBDatabase.createObjectStore db "store" storeParams
294− _ <- putVar var { db, store }
295− pure unit
296−
297− var <- makeVar
298− db <- IDBFactory.open "db" Nothing
299− { onUpgradeNeeded : Just (onUpgradeNeeded var)
300− , 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
301328 }
302329
303− takeVar var
304−
305− it "createObjectStore (keyPath: [], autoIncrement: true)" do
306− { db, store } <- setup { keyPath: [], autoIncrement: true }
307− IDBObjectStore.name store `shouldEqual` "store"
308− IDBObjectStore.keyPath store `shouldEqual` []
309− IDBObjectStore.autoIncrement store `shouldEqual` true
310− IDBObjectStore.indexNames store `shouldEqual` []
311− tearDown db
312−
313− it "createObjectStore (keyPath: [\"patate\"], autoIncrement: true)" do
314− { db, store } <- setup { keyPath: ["patate"], autoIncrement: true }
315− IDBObjectStore.name store `shouldEqual` "store"
316− IDBObjectStore.keyPath store `shouldEqual` ["patate"]
317− IDBObjectStore.autoIncrement store `shouldEqual` true
318− IDBObjectStore.indexNames store `shouldEqual` []
319− tearDown db
320−
321− it "createObjectStore (keyPath: [\"a\", \"b\"], autoIncrement: false)" do
322− { db, store } <- setup { keyPath: ["patate", "autruche"], autoIncrement: false }
323− IDBObjectStore.name store `shouldEqual` "store"
324− IDBObjectStore.keyPath store `shouldEqual` ["patate", "autruche"]
325− IDBObjectStore.autoIncrement store `shouldEqual` false
326− IDBObjectStore.indexNames store `shouldEqual` []
327− tearDown db
328−
329− it "deleteObjectStore" do
330− 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
331359 _ <- IDBDatabase.deleteObjectStore db "store"
332− putVar var true
333−
334− var <- makeVar
335− { db, store } <- setup IDBObjectStore.defaultParameters
336− IDBDatabase.close db
337− db <- IDBFactory.open "db" (Just 999) { onUpgradeNeeded : Just (onUpgradeNeeded var)
338− , onBlocked : Nothing
339− }
340− deleted <- takeVar var
341− deleted `shouldEqual` true
342− tearDown db
343−
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'
344372
345− describe "IDBObjectStore" do
346− let
373+ describe "IDBObjectStore" do
374+ let
347375 tearDown db = do
348376 IDBDatabase.close db
349377 _ <- IDBFactory.deleteDatabase (IDBDatabase.name db)
350378 pure unit
351379
352380 setup { storeParams, onUpgradeNeeded } = do
353− let onUpgradeNeeded' var db _ = launchAff' do
354− store <- IDBDatabase.createObjectStore db "store" storeParams
355− liftEff $ maybe (pure unit) id (onUpgradeNeeded <*> pure db <*> pure store)
356− putVar var { db, store }
357−
358− var <- makeVar
359− db <- IDBFactory.open "db" Nothing
360− { onUpgradeNeeded : Just (onUpgradeNeeded' var)
361− , onBlocked : Nothing
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
362391 }
363392
364− takeVar var
365−
366− it "add()" do
367− date <- liftEff $ toDateTime <$> now
368− { db } <- setup
369− { storeParams: { autoIncrement: true, keyPath: [] }
370− , onUpgradeNeeded: Just $ \_ store -> launchAff' do
371− -- no key
372− key <- IDBObjectStore.add store "patate" none
373− (toKey 1) `shouldEqual` key
374−
375− -- int key
376− key <- IDBObjectStore.add store "patate" (Just 14)
377− (toKey 14) `shouldEqual` key
378−
379− -- number key
380− key <- IDBObjectStore.add store "patate" (Just 14.42)
381− (toKey 14.42) `shouldEqual` key
382−
383− -- string key
384− key <- IDBObjectStore.add store "patate" (Just "key")
385− (toKey "key") `shouldEqual` key
386−
387− -- date key
388− key <- IDBObjectStore.add store "patate" (Just date)
389− (toKey date) `shouldEqual` key
390−
391− -- array key
392− key <- IDBObjectStore.add store "patate" (Just $ toKey [14, 42])
393− (toKey [14, 42]) `shouldEqual` key
394− }
395− tearDown db
396−
397− it "clear()" do
398− { db } <- setup
399− { storeParams: IDBObjectStore.defaultParameters
400− , onUpgradeNeeded: Just $ \_ store -> launchAff' do
401− key <- IDBObjectStore.add store "patate" (Just 14)
402− _ <- IDBObjectStore.clear store
403− val <- IDBObjectStore.get store (IDBKeyRange.only key)
404− val `shouldEqual` (Nothing :: Maybe String)
405− }
406− tearDown db
407−
408− it "count()" do
409− { db } <- setup
410− { storeParams: IDBObjectStore.defaultParameters
411− , onUpgradeNeeded: Just $ \_ store -> launchAff' do
412− key <- IDBObjectStore.add store "patate" (Just 14)
413− key <- IDBObjectStore.add store "autruche" (Just 42)
414− n <- IDBObjectStore.count store Nothing
415− n `shouldEqual` 2
416− }
417− tearDown db
418−
419− it "getKey()" do
420− { db } <- setup
421− { storeParams: IDBObjectStore.defaultParameters
422− , onUpgradeNeeded: Just $ \_ store -> launchAff' do
423− key <- IDBObjectStore.add store "patate" (Just 14)
424− mkey <- IDBObjectStore.getKey store (IDBKeyRange.only 14)
425− mkey `shouldEqual` (Just key)
426−
427− mkey <- IDBObjectStore.getKey store (IDBKeyRange.only 42)
428− mkey `shouldEqual` none
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''
411+
412+ -- string key
413+ key''' <- IDBObjectStore.add store "patate" (Just "key")
414+ (toKey "key") `shouldEqual` key'''
415+
416+ -- date key
417+ key'''' <- IDBObjectStore.add store "patate" (Just date)
418+ (toKey date) `shouldEqual` key''''
419+
420+ -- array key
421+ key''''' <- IDBObjectStore.add store "patate" (Just $ toKey [ 14, 42 ])
422+ (toKey [ 14, 42 ]) `shouldEqual` key'''''
423+ }
424+ tearDown db
425+
426+ it "clear()" do
427+ { db } <- setup
428+ { storeParams: IDBDatabase.defaultParameters
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)
429434 }
430− tearDown db
431−
432− it "getAllKeys()" do
433− { db } <- setup
434− { storeParams: IDBObjectStore.defaultParameters
435− , onUpgradeNeeded: Just $ \_ store -> launchAff' do
436− key1 <- IDBObjectStore.add store "patate" (Just 14)
437− key2 <- IDBObjectStore.add store "autruche" (Just 42)
438− key3 <- IDBObjectStore.add store 14 (Just 1337)
439−
440− -- no bounds
441− keys <- IDBObjectStore.getAllKeys store Nothing Nothing
442− keys `shouldEqual` [key1, key2, key3]
443−
444− -- lower bound
445− keys <- IDBObjectStore.getAllKeys store (Just $ IDBKeyRange.lowerBound 14 true) Nothing
446− keys `shouldEqual` [key2, key3]
447−
448− -- upper bound
449− keys <- IDBObjectStore.getAllKeys store (Just $ IDBKeyRange.upperBound 42 false) Nothing
450− keys `shouldEqual` [key1, key2]
451−
452− -- count
453− keys <- IDBObjectStore.getAllKeys store (Just $ IDBKeyRange.lowerBound 1 true) (Just 2)
454− keys `shouldEqual` [key1, key2]
455− }
456− tearDown db
457−
458− it "openCursor()" do
459− let cb =
460− { onSuccess : const $ pure unit
461− , onError : show >>> fail >>> launchAff'
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'
462492 , onComplete: pure unit
463493 }
464− { db } <- setup
465− { storeParams: IDBObjectStore.defaultParameters
466− , onUpgradeNeeded: Just $ \_ store -> launchAff' do
467− _ <- IDBObjectStore.openCursor store Nothing Next cb
468− _ <- IDBObjectStore.openCursor store Nothing NextUnique cb
469− _ <- IDBObjectStore.openCursor store Nothing Prev cb
470− _ <- IDBObjectStore.openCursor store Nothing PrevUnique cb
471− _ <- IDBObjectStore.openCursor store (Just $ IDBKeyRange.upperBound 1 true) Next cb
472− pure unit
473− }
474− tearDown db
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
475505
476− it "openKeyCursor()" do
477− let cb =
478− { onSuccess : const $ pure unit
479− , onError : show >>> fail >>> launchAff'
506+ it "openKeyCursor()" do
507+ let
508+ cb =
509+ { onSuccess: const $ pure unit
510+ , onError: show >>> fail >>> launchAff'
480511 , onComplete: pure unit
481512 }
482− { db } <- setup
483− { storeParams: IDBObjectStore.defaultParameters
484− , onUpgradeNeeded: Just $ \_ store -> launchAff' do
485− _ <- IDBObjectStore.openKeyCursor store Nothing Next cb
486− _ <- IDBObjectStore.openKeyCursor store Nothing NextUnique cb
487− _ <- IDBObjectStore.openKeyCursor store Nothing Prev cb
488− _ <- IDBObjectStore.openKeyCursor store Nothing PrevUnique cb
489− _ <- IDBObjectStore.openKeyCursor store (Just $ IDBKeyRange.lowerBound 1 true) Next cb
490− pure unit
491− }
492− tearDown db
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
493524
494− describe "IDBIndex" do
495− let
525+ describe "IDBIndex" do
526+ let
496527 tearDown db = do
497528 IDBDatabase.close db
498529 _ <- IDBFactory.deleteDatabase (IDBDatabase.name db)
499530 pure unit
500531
501− setup :: forall value e e'.
502− { storeParams :: { keyPath :: Array String, autoIncrement :: Boolean }
503− , indexParams :: { unique :: Boolean, multiEntry :: Boolean }
504− , values :: Array (Tuple value (Maybe Key))
505− , keyPath :: Array String
506− , onUpgradeNeeded :: Maybe (Database -> Transaction -> Index -> Eff (idb :: IDB, avar :: AVAR, exception :: EXCEPTION | e') Unit)
507− } -> Aff (idb :: IDB, avar :: AVAR | e) { db :: Database, index :: Index, store :: ObjectStore }
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 }
508541 setup { storeParams, indexParams, values, keyPath, onUpgradeNeeded } = do
509− let onUpgradeNeeded' var db tx = launchAff' do
510− store <- IDBDatabase.createObjectStore db "store" storeParams
511− _ <- traverse (uncurry (IDBObjectStore.add store)) values
512− index <- IDBObjectStore.createIndex store "index" keyPath indexParams
513− liftEff $ maybe (pure unit) id (onUpgradeNeeded <*> pure db <*> pure tx <*> pure index)
514− putVar var { db, index, store }
515−
516− var <- makeVar
517− db <- IDBFactory.open "db" Nothing
518− { onUpgradeNeeded : Just (onUpgradeNeeded' var)
519− , onBlocked : Nothing
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
520554 }
521− takeVar var
522−
523−
524− it "returns an IDBIndex and the properties are set correctly" do
525− { db, index } <- setup
526− { storeParams : IDBObjectStore.defaultParameters
527− , indexParams : IDBIndex.defaultParameters
528− , onUpgradeNeeded : Nothing
529− , keyPath : []
530− , values : []
531− }
532− IDBIndex.name index `shouldEqual` "index"
533− IDBIndex.keyPath index `shouldEqual` []
534− IDBIndex.unique index `shouldEqual` IDBIndex.defaultParameters.unique
535− IDBIndex.multiEntry index `shouldEqual` IDBIndex.defaultParameters.multiEntry
536− tearDown db
537−
538− it "attempt to create an index that requires unique values on an object store already contains duplicates" do
539− let onAbort var = launchAff' (putVar var true)
540− txVar <- makeVar
541− dbVar <- makeVar
542− res <- attempt $ setup
543− { storeParams : IDBObjectStore.defaultParameters
544− , indexParams : { unique : true
545− , multiEntry : false
546− }
547− , keyPath : ["indexedProperty"]
548− , values : [ { indexedProperty: "bar" } :+: (Just $ toKey 1)
549− , { indexedProperty: "bar" } :+: (Just $ toKey 2)
550− ]
551− , onUpgradeNeeded : Just $ \db tx _ -> launchAff' do
552− IDBTransaction.onAbort tx (onAbort txVar)
553− IDBDatabase.onAbort db (onAbort dbVar)
554− }
555− case res of
556− Right _ ->
557− fail "expected abort"
558− _ -> do
559− shouldEqual true =<< takeVar txVar
560− shouldEqual true =<< takeVar dbVar
561−
562−
563− it "the index is usable right after being made" do
564− { db } <- setup
565− { storeParams : { keyPath : ["key"]
566− , autoIncrement : false
567− }
568− , indexParams : IDBIndex.defaultParameters
569− , keyPath : ["indexedProperty"]
570− , values : [ { key: "key1", indexedProperty: "indexed_1" } :+: Nothing
571− , { key: "key2", indexedProperty: "indexed_2" } :+: Nothing
572− , { key: "key3", indexedProperty: "indexed_3" } :+: Nothing
573− ]
574− , onUpgradeNeeded : Just $ \_ _ index -> launchAff' do
575− val <- IDBIndex.get index (IDBKeyRange.only "indexed_2")
576− ((\r -> r.key) <$> val) `shouldEqual` (Just $ toKey "key2")
577− }
578− tearDown db
579−
580− it "empty keyPath" do
581− { db } <- setup
582− { storeParams : IDBObjectStore.defaultParameters
583− , indexParams : IDBIndex.defaultParameters
584− , keyPath : []
585− , values : [ "object_1" :+: (Just $ toKey 1)
586− , "object_2" :+: (Just $ toKey 2)
587− , "object_3" :+: (Just $ toKey 3)
588− ]
589− , onUpgradeNeeded : Just $ \_ _ index -> launchAff' do
590− val <- IDBIndex.get index (IDBKeyRange.only "object_3")
591− val `shouldEqual` (Just "object_3")
592− }
593− tearDown db
594−
595− it "index can be valid keys [date]" do
596− date <- liftEff $ toDateTime <$> now
597− { db } <- setup
598− { storeParams : { keyPath: ["key"]
599− , autoIncrement: false
600− }
601− , indexParams : IDBIndex.defaultParameters
602− , keyPath : ["i"]
603− , values : [ { key: "date", i: (toKey date) } :+: Nothing
604− ]
605− , onUpgradeNeeded : Just $ \_ _ index -> launchAff' do
606− val <- IDBIndex.get index (IDBKeyRange.only date)
607− ((\r -> r.key) <$> val) `shouldEqual` (Just "date")
608− }
609− tearDown db
610−
611− it "index can be valid keys [num]" do
612− let num = 14
613− { db } <- setup
614− { storeParams : { keyPath: ["key"]
615− , autoIncrement: false
616− }
617− , indexParams : IDBIndex.defaultParameters
618− , keyPath : ["i"]
619− , values : [ { key: "num", i: (toKey num) } :+: Nothing
620− ]
621− , onUpgradeNeeded : Just $ \_ _ index -> launchAff' do
622− val <- IDBIndex.get index (IDBKeyRange.only num)
623− ((\r -> r.key) <$> val) `shouldEqual` (Just "num")
624− }
625− tearDown db
626−
627− it "index can be valid keys [array]" do
628− let array = ["patate", "autruche"]
629− { db } <- setup
630− { storeParams : { keyPath: ["key"]
631− , autoIncrement: false
632− }
633− , indexParams : IDBIndex.defaultParameters
634− , keyPath : ["i"]
635− , values : [ { key: "array", i: (toKey array) } :+: Nothing
636− ]
637− , onUpgradeNeeded : Just $ \_ _ index -> launchAff' do
638− val <- IDBIndex.get index (IDBKeyRange.only array)
639− ((\r -> r.key) <$> val) `shouldEqual` (Just "array")
640− }
641− tearDown db
642−
643− it "openKeyCursor() - throw InvalidStateError on index deleted by aborted upgrade" do
644− res <- attempt $ setup
645− { storeParams : { keyPath: ["key"], autoIncrement: false }
646− , indexParams : IDBIndex.defaultParameters
647− , keyPath : ["indexedProperty"]
648− , values : [ { key: 14, indexedProperty: "patate" } :+: Nothing
649− ]
650− , onUpgradeNeeded : Just $ \db tx index -> launchAff' do
651− let cb =
652− { onSuccess : const $ pure unit
653− , onError : show >>> fail >>> launchAff'
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'
654699 , onComplete: pure unit
655700 }
656− IDBTransaction.onAbort tx (pure unit)
657− IDBDatabase.onAbort db (pure unit)
658− IDBTransaction.abort tx
659− cursor <- attempt $ IDBIndex.openKeyCursor index Nothing Next cb
660− case cursor of
661− Right _ -> fail "expected InvalidStateError"
662− Left err -> message err `shouldEqual` "InvalidStateError"
663− }
664− case res of
665− Right _ -> fail "expected InvalidStateError"
666− _ -> pure unit
667−
668− it "openKeyCursor() - throw TransactionInactiveError on aborted transaction" do
669− { db } <- setup
670− { storeParams : { keyPath: ["key"], autoIncrement: false }
671− , indexParams : IDBIndex.defaultParameters
672− , keyPath : ["indexedProperty"]
673− , values : [ { key: 14, indexedProperty: "patate" } :+: Nothing
674− ]
675− , onUpgradeNeeded : Nothing
676− }
677− let cb =
678− { onSuccess : const $ pure unit
679− , onError : show >>> fail >>> launchAff'
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'
680727 , onComplete: pure unit
681728 }
682− tx <- IDBDatabase.transaction db ["store"] ReadOnly
683− store <- IDBTransaction.objectStore tx "store"
684− index <- IDBObjectStore.index store "index"
685− IDBTransaction.onAbort tx (pure unit)
686− IDBTransaction.abort tx
687− cursor <- attempt $ IDBIndex.openKeyCursor index Nothing Next cb
688− case cursor of
689− Right _ -> fail "expected TransactionInactiveError"
690− Left err -> message err `shouldEqual` "TransactionInactiveError"
691−
692− tearDown db
693−
694− it "openKeyCursor() - throw InvalidStateError when the index is deleted" do
695− { db } <- setup
696− { storeParams : { keyPath: ["key"], autoIncrement: false }
697− , indexParams : IDBIndex.defaultParameters
698− , keyPath : ["indexedProperty"]
699− , values : [ { key: 14, indexedProperty: "patate" } :+: Nothing
700− ]
701− , onUpgradeNeeded : Just $ \db tx index -> launchAff' do
702− let cb =
703− { onSuccess : const $ pure unit
704− , onError : show >>> fail >>> launchAff'
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'
705754 , onComplete: pure unit
706755 }
707− store <- IDBTransaction.objectStore tx "store"
708− IDBObjectStore.deleteIndex store "index"
709− cursor <- attempt $ IDBIndex.openKeyCursor index Nothing Next cb
710− case cursor of
711− Right _ -> fail "expected InvalidStateError"
712− Left err -> message err `shouldEqual` "InvalidStateError"
713− }
714−
715− tearDown db
716−
717− it "openCursor() - throw InvalidStateError on index deleted by aborted upgrade" do
718− res <- attempt $ setup
719− { storeParams : { keyPath: ["key"], autoIncrement: false }
720− , indexParams : IDBIndex.defaultParameters
721− , keyPath : ["indexedProperty"]
722− , values : [ { key: 14, indexedProperty: "patate" } :+: Nothing
723− ]
724− , onUpgradeNeeded : Just $ \db tx index -> launchAff' do
725− let cb =
726− { onSuccess : const $ pure unit
727− , onError : show >>> fail >>> launchAff'
756+ store <- IDBTransaction.objectStore tx "store"
757+ IDBObjectStore.deleteIndex store "index"
758+ cursor <- attempt $ IDBIndex.openKeyCursor index Nothing Next cb
759+ case cursor of
760+ Right _ -> fail "expected InvalidStateError"
761+ Left err -> name err `shouldEqual` "InvalidStateError"
762+ }
763+
764+ tearDown db
765+
766+ it "openCursor() - throw InvalidStateError on index deleted by aborted upgrade" do
767+ res <- attempt $ setup
768+ { storeParams: { keyPath: [ "key" ], autoIncrement: false }
769+ , indexParams: IDBObjectStore.defaultParameters
770+ , keyPath: [ "indexedProperty" ]
771+ , values:
772+ [ { key: 14, indexedProperty: "patate" } :+: Nothing
773+ ]
774+ , onUpgradeNeeded: Just $ \db tx index -> launchAff' do
775+ let
776+ cb =
777+ { onSuccess: const $ pure unit
778+ , onError: show >>> fail >>> launchAff'
728779 , onComplete: pure unit
729780 }
730− IDBTransaction.onAbort tx (pure unit)
731− IDBDatabase.onAbort db (pure unit)
732− IDBTransaction.abort tx
733− cursor <- attempt $ IDBIndex.openCursor index Nothing Next cb
734− case cursor of
735− Right _ -> fail "expected InvalidStateError"
736− Left err -> message err `shouldEqual` "InvalidStateError"
737− }
738− case res of
739− Right _ -> fail "expected InvalidStateError"
740− _ -> pure unit
741−
742− it "openCursor() - throw TransactionInactiveError on aborted transaction" do
743− { db } <- setup
744− { storeParams : { keyPath: ["key"], autoIncrement: false }
745− , indexParams : IDBIndex.defaultParameters
746− , keyPath : ["indexedProperty"]
747− , values : [ { key: 14, indexedProperty: "patate" } :+: Nothing
748− ]
749− , onUpgradeNeeded : Nothing
750− }
751− let cb =
752− { onSuccess : const $ pure unit
753− , onError : show >>> fail >>> launchAff'
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'
754807 , onComplete: pure unit
755808 }
756− tx <- IDBDatabase.transaction db ["store"] ReadOnly
757− store <- IDBTransaction.objectStore tx "store"
758− index <- IDBObjectStore.index store "index"
759− IDBTransaction.onAbort tx (pure unit)
760− IDBTransaction.abort tx
761− cursor <- attempt $ IDBIndex.openCursor index Nothing Next cb
762− case cursor of
763− Right _ -> fail "expected TransactionInactiveError"
764− Left err -> message err `shouldEqual` "TransactionInactiveError"
765−
766− tearDown db
767−
768− it "openCursor() - throw InvalidStateError when the index is deleted" do
769− { db } <- setup
770− { storeParams : { keyPath: ["key"], autoIncrement: false }
771− , indexParams : IDBIndex.defaultParameters
772− , keyPath : ["indexedProperty"]
773− , values : [ { key: 14, indexedProperty: "patate" } :+: Nothing
774− ]
775− , onUpgradeNeeded : Just $ \db tx index -> launchAff' do
776− let cb =
777− { onSuccess : const $ pure unit
778− , onError : show >>> fail >>> launchAff'
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'
779834 , onComplete: pure unit
780835 }
781− store <- IDBTransaction.objectStore tx "store"
782− IDBObjectStore.deleteIndex store "index"
783− cursor <- attempt $ IDBIndex.openCursor index Nothing Next cb
784− case cursor of
785− Right _ -> fail "expected InvalidStateError"
786− Left err -> message err `shouldEqual` "InvalidStateError"
787− }
788−
789− tearDown db
790−
791− it "getKey() - multiEntry - adding keys" do
792− { db } <- setup
793− { storeParams : IDBObjectStore.defaultParameters
794− , indexParams : { unique: false, multiEntry: true }
795− , keyPath : ["name"]
796− , values : [ { name: ["patate", "autruche"] } :+: (Just $ toKey 1)
797− , { name: ["bob"] } :+: (Just $ toKey 2)
798− ]
799− , onUpgradeNeeded : Just $ \_ _ index -> launchAff' do
800− key <- IDBIndex.getKey index (IDBKeyRange.only "patate")
801− key `shouldEqual` (Just $ toKey 1)
802−
803− key <- IDBIndex.getKey index (IDBKeyRange.only "autruche")
804− key `shouldEqual` (Just $ toKey 1)
805−
806− key <- IDBIndex.getKey index (IDBKeyRange.only "bob")
807− key `shouldEqual` (Just $ toKey 2)
808− }
809− tearDown db
810−
811−
812− it "get() - returns the record with the first key in the range" do
813− { db } <- setup
814− { storeParams : { keyPath: ["key"], autoIncrement: false }
815− , indexParams : IDBIndex.defaultParameters
816− , keyPath : ["indexedProperty"]
817− , values : [ { key: 14, indexedProperty: "patate" } :+: Nothing
818− , { key: 42, indexedProperty: "autruche" } :+: Nothing
819− , { key: 1337, indexedProperty: "baguette" } :+: Nothing
820− ]
821− , onUpgradeNeeded : Nothing
822− }
823−
824− tx <- IDBDatabase.transaction db ["store"] ReadOnly
825− store <- IDBTransaction.objectStore tx "store"
826− index <- IDBObjectStore.index store "index"
827− val <- IDBIndex.get index (IDBKeyRange.lowerBound "autruche" false)
828− ((\r -> r.key) <$> val) `shouldEqual` (Just 42)
829−
830− tearDown db
831−
832− describe "IDBCursor" do
833− let
836+ store <- IDBTransaction.objectStore tx "store"
837+ IDBObjectStore.deleteIndex store "index"
838+ cursor <- attempt $ IDBIndex.openCursor index Nothing Next cb
839+ case cursor of
840+ Right _ -> fail "expected InvalidStateError"
841+ Left err -> name err `shouldEqual` "InvalidStateError"
842+ }
843+
844+ tearDown db
845+
846+ it "getKey() - multiEntry - adding keys" do
847+ { db } <- setup
848+ { storeParams: IDBDatabase.defaultParameters
849+ , indexParams: { unique: false, multiEntry: true }
850+ , keyPath: [ "name" ]
851+ , values:
852+ [ { name: [ "patate", "autruche" ] } :+: (Just $ toKey 1)
853+ , { name: [ "bob" ] } :+: (Just $ toKey 2)
854+ ]
855+ , onUpgradeNeeded: Just $ \_ _ index -> launchAff' do
856+ key <- IDBIndex.getKey index (IDBKeyRange.only "patate")
857+ key `shouldEqual` (Just $ toKey 1)
858+
859+ key' <- IDBIndex.getKey index (IDBKeyRange.only "autruche")
860+ key' `shouldEqual` (Just $ toKey 1)
861+
862+ key'' <- IDBIndex.getKey index (IDBKeyRange.only "bob")
863+ key'' `shouldEqual` (Just $ toKey 2)
864+ }
865+ tearDown db
866+
867+ it "get() - returns the record with the first key in the range" do
868+ { db } <- setup
869+ { storeParams: { keyPath: [ "key" ], autoIncrement: false }
870+ , indexParams: IDBObjectStore.defaultParameters
871+ , keyPath: [ "indexedProperty" ]
872+ , values:
873+ [ { key: 14, indexedProperty: "patate" } :+: Nothing
874+ , { key: 42, indexedProperty: "autruche" } :+: Nothing
875+ , { key: 1337, indexedProperty: "baguette" } :+: Nothing
876+ ]
877+ , onUpgradeNeeded: Nothing
878+ }
879+
880+ tx <- IDBDatabase.transaction db [ "store" ] ReadOnly
881+ store <- IDBTransaction.objectStore tx "store"
882+ index <- IDBObjectStore.index store "index"
883+ val <- IDBIndex.get index (IDBKeyRange.lowerBound "autruche" false)
884+ ((\r -> r.key) <$> val) `shouldEqual` (Just 42)
885+
886+ tearDown db
887+
888+ describe "IDBCursor" do
889+ let
834890 tearDown db = do
835891 IDBDatabase.close db
836892 _ <- IDBFactory.deleteDatabase (IDBDatabase.name db)
837893 pure unit
838894
839− setup :: forall value e e'.
840− { storeParams :: { keyPath :: Array String, autoIncrement :: Boolean }
841− , indexParams :: { unique :: Boolean, multiEntry :: Boolean }
842− , values :: Array (Tuple value (Maybe Key))
843− , keyPath :: Array String
844− , onUpgradeNeeded :: Maybe (Database -> Transaction -> Index -> Eff (idb :: IDB, avar :: AVAR, exception :: EXCEPTION | e') Unit)
845− } -> 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 }
846904 setup { storeParams, indexParams, values, keyPath, onUpgradeNeeded } = do
847− let onUpgradeNeeded' var db tx = launchAff' do
848− store <- IDBDatabase.createObjectStore db "store" storeParams
849− _ <- traverse (uncurry (IDBObjectStore.add store)) values
850− index <- IDBObjectStore.createIndex store "index" keyPath indexParams
851− liftEff $ maybe (pure unit) id (onUpgradeNeeded <*> pure db <*> pure tx <*> pure index)
852− putVar var { db, index, store }
853−
854− var <- makeVar
855− db <- IDBFactory.open "db" Nothing
856− { onUpgradeNeeded : Just (onUpgradeNeeded' var)
857− , 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
858917 }
859− takeVar var
860−
861− it "continue() - iterate to the next record" do
862− { db } <- setup
863− { storeParams : IDBObjectStore.defaultParameters
864− , indexParams : IDBIndex.defaultParameters
865− , keyPath : []
866− , values : [ "cupcake" :+: (Just $ toKey 4)
867− , "pancake" :+: (Just $ toKey 2)
868− , "pie" :+: (Just $ toKey 1)
869− , "pie" :+: (Just $ toKey 3)
870− ]
871− , onUpgradeNeeded: Nothing
872− }
873− let cb vdone vvals =
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 =
874935 { onComplete: launchAff' do
875− putVar vdone unit
936+ AVar.put unit vdone
876937
877− , onError: \error -> launchAff' do
938+ , onError: \error -> launchAff' do
878939 fail $ "unexpected error: " <> show error
879940
880941 , onSuccess: \cursor -> launchAff' do
881− vals <- takeVar vvals
942+ vals <- AVar.take vvals
882943 pure (IDBCursor.value cursor) >>= shouldEqual (maybe "" _.v $ head vals)
883− IDBCursor.primaryKey cursor >>= shouldEqual (maybe (toKey 0) _.k $ head vals)
884− putVar vvals (drop 1 vals)
944+ IDBCursor.primaryKey cursor >>= shouldEqual (maybe (toKey 0) _.k $ head vals)
945+ AVar.put (drop 1 vals) vvals
885946 IDBCursor.continue cursor none
886947 }
887− vdone <- makeVar
888− vvals <- makeVar'
889− [ { v: "pie" , k: toKey 1 }
890− , { v: "pancake", k: toKey 2 }
891− , { v: "pie" , k: toKey 3 }
892− , { v: "cupcake", k: toKey 4 }
893− ]
894− tx <- IDBDatabase.transaction db ["store"] ReadOnly
895− store <- IDBTransaction.objectStore tx "store"
896− IDBObjectStore.openCursor store Nothing Next (cb vdone vvals)
897− takeVar vdone
898− tearDown db
899−
900− it "continue() - attempt to iterate in the wrong direction" do
901− { db } <- setup
902− { storeParams : IDBObjectStore.defaultParameters
903− , indexParams : IDBIndex.defaultParameters
904− , keyPath : []
905− , values : [ "cupcake" :+: (Just $ toKey 4)
906− , "pancake" :+: (Just $ toKey 2)
907− , "pie" :+: (Just $ toKey 1)
908− , "pie" :+: (Just $ toKey 3)
909− ]
910− , onUpgradeNeeded: Nothing
911− }
912− let cb vdone =
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 =
913976 { onComplete: launchAff' do
914977 fail $ "shouldn't complete"
915978
916− , onError: \error -> launchAff' do
979+ , onError: \error -> launchAff' do
917980 fail $ "unexpected error: " <> show error
918981
919982 , onSuccess: \cursor -> launchAff' do
920983 res <- attempt $ IDBCursor.continue cursor (Just 1)
921984 case res of
922− Left err -> do
923− message err `shouldEqual` "DataError"
924− putVar vdone unit
985+ Left err -> do
986+ name err `shouldEqual` "DataError"
987+ AVar.put unit vdone
925988 Right _ -> do
926989 fail "expected continue to fail"
927990 }
928− vdone <- makeVar
929− tx <- IDBDatabase.transaction db ["store"] ReadOnly
930− store <- IDBTransaction.objectStore tx "store"
931− IDBObjectStore.openCursor store Nothing Next (cb vdone)
932− takeVar vdone
933− tearDown db
934−
935−
936− it "advance() - iterate cursor number of times specified by count" do
937− { db } <- setup
938− { storeParams : { keyPath: ["pKey"], autoIncrement: false }
939− , indexParams : IDBIndex.defaultParameters
940− , keyPath : []
941− , values : [ { pKey: "pkey_0", iKey: "ikey_0" } :+: Nothing
942− , { pKey: "pkey_1", iKey: "ikey_1" } :+: Nothing
943− , { pKey: "pkey_2", iKey: "ikey_2" } :+: Nothing
944− , { pKey: "pkey_3", iKey: "ikey_3" } :+: Nothing
945− ]
946− , onUpgradeNeeded: Nothing
947− }
948− let cb vdone vjump =
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 =
9491013 { onComplete: launchAff' do
950− putVar vdone unit
1014+ AVar.put unit vdone
9511015
952− , onError: \error -> launchAff' do
1016+ , onError: \error -> launchAff' do
9531017 fail $ "unexpected error: " <> show error
9541018
9551019 , onSuccess: \cursor -> launchAff' do
956− jump <- takeVar vjump
957− if jump
958− then do
959− IDBCursor.advance cursor 3
960− else do
961− let value = IDBCursor.value cursor
962− value.pKey `shouldEqual` "pkey_3"
963− value.iKey `shouldEqual` "ikey_3"
964− IDBCursor.continue cursor none
965− putVar vjump false
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
9661029 }
967− vdone <- makeVar
968− vjump <- makeVar' true
969− tx <- IDBDatabase.transaction db ["store"] ReadOnly
970− store <- IDBTransaction.objectStore tx "store"
971− IDBObjectStore.openCursor store Nothing Next (cb vdone vjump)
972− takeVar vdone
973− tearDown db
974−
975− it "delete() - remove a record from the object store" do
976− { db } <- setup
977− { storeParams : { keyPath: ["pKey"], autoIncrement: false }
978− , indexParams : IDBIndex.defaultParameters
979− , keyPath : []
980− , values : [ { pKey: "pkey_0", iKey: "ikey_0" } :+: Nothing
981− , { pKey: "pkey_1", iKey: "ikey_1" } :+: Nothing
982− , { pKey: "pkey_2", iKey: "ikey_2" } :+: Nothing
983− , { pKey: "pkey_3", iKey: "ikey_3" } :+: Nothing
984− ]
985− , onUpgradeNeeded: Nothing
986− }
987− let cb vdone store =
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 =
9881053 { onComplete: launchAff' do
9891054 mval <- map _.pKey <$> IDBIndex.get store (IDBKeyRange.only "pkey_0")
9901055 mval `shouldEqual` (Nothing :: Maybe String)
991− putVar vdone unit
1056+ AVar.put unit vdone
9921057
993− , onError: \error -> launchAff' do
1058+ , onError: \error -> launchAff' do
9941059 fail $ "unexpected error: " <> show error
9951060
9961061 , onSuccess: \cursor -> launchAff' do
@@ -999,29 +1064,31 @@ main = runMocha do
9991064 IDBCursor.delete cursor
10001065 IDBCursor.advance cursor 4
10011066 }
1002− vdone <- makeVar
1003− tx <- IDBDatabase.transaction db ["store"] ReadWrite
1004− store <- IDBTransaction.objectStore tx "store"
1005− IDBObjectStore.openCursor store Nothing Next (cb vdone store)
1006− takeVar vdone
1007− tearDown db
1008−
1009− it "update() - modify a record in the object store" do
1010− { db } <- setup
1011− { storeParams : { keyPath: ["pKey"], autoIncrement: false }
1012− , indexParams : IDBIndex.defaultParameters
1013− , keyPath : []
1014− , values : [ { pKey: "pkey_0", iKey: "ikey_0" } :+: Nothing
1015− ]
1016− , onUpgradeNeeded: Nothing
1017− }
1018− let cb vdone store =
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 =
10191086 { onComplete: launchAff' do
10201087 mval <- map _.iKey <$> IDBIndex.get store (IDBKeyRange.only "pkey_0")
10211088 mval `shouldEqual` (Just "patate")
1022− putVar vdone unit
1089+ AVar.put unit vdone
10231090
1024− , onError: \error -> launchAff' do
1091+ , onError: \error -> launchAff' do
10251092 fail $ "unexpected error: " <> show error
10261093
10271094 , onSuccess: \cursor -> launchAff' do
@@ -1031,41 +1098,44 @@ main = runMocha do
10311098 key `shouldEqual` toKey "pkey_0"
10321099 IDBCursor.advance cursor 4
10331100 }
1034− vdone <- makeVar
1035− tx <- IDBDatabase.transaction db ["store"] ReadWrite
1036− store <- IDBTransaction.objectStore tx "store"
1037− IDBObjectStore.openCursor store Nothing Next (cb vdone store)
1038− takeVar vdone
1039− tearDown db
1040−
1041− it "update() - throw ReadOnlyError after update on ReadOnly transaction" do
1042− { db } <- setup
1043− { storeParams : { keyPath: ["pKey"], autoIncrement: false }
1044− , indexParams : IDBIndex.defaultParameters
1045− , keyPath : []
1046− , values : [ { pKey: "pkey_0", iKey: "ikey_0" } :+: Nothing
1047− ]
1048− , onUpgradeNeeded: Nothing
1049− }
1050− let cb vdone =
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 =
10511120 { onComplete: launchAff' do
10521121 fail $ "shouldn't complete"
10531122
1054− , onError: \error -> launchAff' do
1123+ , onError: \error -> launchAff' do
10551124 fail $ "unexpected error: " <> show error
10561125
10571126 , onSuccess: \cursor -> launchAff' do
10581127 res <- attempt $ IDBCursor.update cursor "patate"
10591128 case res of
10601129 Left err -> do
1061− message err `shouldEqual` "ReadOnlyError"
1062− putVar vdone unit
1130+ name err `shouldEqual` "ReadOnlyError"
1131+ AVar.put unit vdone
10631132 Right _ ->
10641133 fail $ "expected ReadOnlyError"
10651134 }
1066− vdone <- makeVar
1067− tx <- IDBDatabase.transaction db ["store"] ReadOnly
1068− store <- IDBTransaction.objectStore tx "store"
1069− IDBObjectStore.openCursor store Nothing Next (cb vdone)
1070− takeVar vdone
1071− tearDown db
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"