git.iko.soy/purescript-indexed-DB
on 54bcd27 help sign in
32 files changed+2378−2594
since splitsnapshots
unifiedsplit
.gitignore−2 README.md+9−41 bower.json+42 deploy.sh+6 index.js−4 karma.browserstack.conf.js+91 karma.conf.js+25 package.json+36−9 packages.dhall−105 spago.dhall−42 src/Database/IndexedDB/Core.js+6−6 src/Database/IndexedDB/Core.purs+88−141 src/Database/IndexedDB/IDBCursor.js+27−63 src/Database/IndexedDB/IDBCursor.purs+162−110 src/Database/IndexedDB/IDBDatabase.js+28−62 src/Database/IndexedDB/IDBDatabase.purs+132−132 src/Database/IndexedDB/IDBFactory.js+8−21 src/Database/IndexedDB/IDBFactory.purs+32−36 src/Database/IndexedDB/IDBIndex.purs+7−197 src/Database/IndexedDB/IDBIndex.js → src/Database/IndexedDB/IDBIndex/Internal.js+28−52 src/Database/IndexedDB/IDBIndex/Internal.purs+256 src/Database/IndexedDB/IDBKey.purs+1−8 src/Database/IndexedDB/IDBKey/Internal.js+3−3 src/Database/IndexedDB/IDBKey/Internal.purs+114−96 src/Database/IndexedDB/IDBKeyRange.js+9−9 src/Database/IndexedDB/IDBKeyRange.purs+79−81 src/Database/IndexedDB/IDBObjectStore.js+28−55 src/Database/IndexedDB/IDBObjectStore.purs+156−160 src/Database/IndexedDB/IDBTransaction.js+18−38 src/Database/IndexedDB/IDBTransaction.purs+83−66 test/Main.purs+904−974 yarn.lock−81
.gitignore−2
@@ -77,5 +77,3 @@ releases
7777.purs*
7878.psa*
7979dist
80−.spago
81−test.js
README.md+9−41
@@ -14,27 +14,8 @@ menaingful namespace (e.g `IDBIndex.get`, `IDBObjectStore.openCursor` ...)
1414
1515Here's a quick example of what it look likes.
1616```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−
3617main :: Eff (idb :: IDB, exception :: EXCEPTION, console :: CONSOLE) Unit
37−main = launchAff_ do
18+main = launchAff' do
3819 db <- IDBFactory.open "db" Nothing { onBlocked : Nothing
3920 , onUpgradeNeeded : Just onUpgradeNeeded
4021 }
@@ -45,21 +26,22 @@ main = launchAff_ do
4526 log $ maybe "not found" id val
4627
4728
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
29+onUpgradeNeeded :: forall e. Database -> Transaction -> Eff (idb :: IDB, exception :: EXCEPTION | e) Unit
30+onUpgradeNeeded db _ = launchAff' do
31+ store <- IDBDatabase.createObjectStore db "store" IDBObjectStore.defaultParameters
5132 _ <- IDBObjectStore.add store "patate" (Just 1)
5233 _ <- IDBObjectStore.add store { property: 42 } (Just 2)
53− _ <- IDBObjectStore.createIndex store "index" ["property"] IDBObjectStore.defaultParameters
34+ _ <- IDBObjectStore.createIndex store "index" ["property"] IDBIndex.defaultParameters
5435 pure unit
5536```
5637
5738## Notes
5839
5940### 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.
41+Errors normally thrown by the IDB\* interfaces are wrapped in the `Aff` Monad as `Error`
42+where the `message` corresponds to the error's name (e.g. "InvalidStateError").
43+Pattern matching can therefore be done on any error message to handle specific errors thrown
44+by the API.
6345
6446### Examples
6547The `test` folder contains a great amount of examples showing practical usage of the IDB\*
@@ -70,20 +52,6 @@ one.
7052
7153## Changelog
7254
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−
86−
8755#### v1.0.0
8856
8957- [Indexed Database API 2.0](https://w3c.github.io/IndexedDB/) totally covered apart from
bower.jsonnew file+42
@@ -0,0 +1,42 @@
1+{
2+ "name": "purescript-indexeddb",
3+ "description": "An API wrapper around IndexedDB",
4+ "version": "1.0.2",
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/truqu/purescript-indexedDB.git"
26+ },
27+ "dependencies": {
28+ "purescript-aff": "^3.1.0",
29+ "purescript-datetime": "^3.2.0",
30+ "purescript-eff": "^3.1.0",
31+ "purescript-exceptions": "https://github.com/truqu/purescript-exceptions.git#3.1.0",
32+ "purescript-maybe": "^3.0.0",
33+ "purescript-prelude": "^3.1.0",
34+ "purescript-read": "^1.0.0"
35+ },
36+ "devDependencies": {
37+ "purescript-spec": "^1.0.0",
38+ "purescript-psci-support": "^3.0.0",
39+ "purescript-now": "^3.0.0",
40+ "purescript-spec-mocha": "https://github.com/truqu/purescript-spec-mocha.git#0.5.0"
41+ }
42+}
deploy.shnew file+6
@@ -0,0 +1,6 @@
1+#!/bin/sh
2+
3+git stash save
4+git clean -df
5+yes | pulp publish --no-push
6+git stash pop
index.jsdeleted−4
@@ -1,4 +0,0 @@
1−import "fake-indexeddb/auto";
2−import * as Main from './output/Test.Main/index.js';
3−
4−Main.main();
karma.browserstack.conf.jsnew file+91
@@ -0,0 +1,91 @@
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.jsnew file+25
@@ -0,0 +1,25 @@
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+36−9
@@ -1,15 +1,42 @@
11{
2− "name": "purescript-indexed-DB",
2+ "name": "purescript-indexeddb",
33 "version": "1.0.0",
4− "main": "index.js",
5− "repository": "https://github.com/ilyakooo0/purescript-indexed-DB.git",
6− "author": "iko <mail@iko.soy>",
7− "license": "MIT",
4+ "description": "A wrapper around the IndexedDB API",
85 "scripts": {
9− "test": "spago build && node index.js"
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"
1031 },
11− "dependencies": {
12− "fake-indexeddb": "^4.0.1"
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"
1340 },
14− "type": "module"
41+ "homepage": "https://github.com/Truqu/purescript-indexeddb#readme"
1542}
packages.dhalldeleted−105
@@ -1,105 +0,0 @@
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.dhalldeleted−42
@@ -1,42 +0,0 @@
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−export function _showCursor(cursor) {
6+exports._showCursor = function _showCursor(cursor) {
77 return '(IDBCursor ' +
88 '{ direction: ' + cursor.direction +
99 ', key: ' + cursor.key +
@@ -11,7 +11,7 @@ export function _showCursor(cursor) {
1111 ' })';
1212};
1313
14−export function _showDatabase(db) {
14+exports._showDatabase = function _showDatabase(db) {
1515 return '(IDBDatabase ' +
1616 '{ name: ' + db.name +
1717 ', objectStoreNames: [' + toArray(db.objectStoreNames).join(', ') + ']' +
@@ -19,7 +19,7 @@ export function _showDatabase(db) {
1919 ' })';
2020};
2121
22−export function _showIndex(index) {
22+exports._showIndex = function _showIndex(index) {
2323 return '(IDBIndex ' +
2424 '{ name: ' + index.name +
2525 ', keyPath: ' + index.keyPath +
@@ -28,7 +28,7 @@ export function _showIndex(index) {
2828 ' })';
2929};
3030
31−export function _showKeyRange(range) {
31+exports._showKeyRange = function _showKeyRange(range) {
3232 return '(IDBKeyRange ' +
3333 '{ lower: ' + range.lower +
3434 ', upper: ' + range.upper +
@@ -37,7 +37,7 @@ export function _showKeyRange(range) {
3737 ' })';
3838};
3939
40−export function _showObjectStore(store) {
40+exports._showObjectStore = function _showObjectStore(store) {
4141 return '(IDBObjectStore ' +
4242 '{ autoIncrement: ' + store.autoIncrement +
4343 ', indexNames: [' + toArray(store.indexNames).join(', ') + ']' +
@@ -46,7 +46,7 @@ export function _showObjectStore(store) {
4646 ' })';
4747};
4848
49−export function _showTransaction(tx) {
49+exports._showTransaction = function _showTransaction(tx) {
5050 return '(IDBTransaction ' +
5151 '{ error: ' + tx.error +
5252 ', mode: ' + tx.mode +
src/Database/IndexedDB/Core.purs+88−141
@@ -1,72 +1,39 @@
11-- | The Core module gathers types used across the library and provides basic Show instances for
22-- | those types.
3−module Database.IndexedDB.Core
4− -- * Effects
5− (
6− -- IDB
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.
79
8− -- * Types
9− Database
10+module Database.IndexedDB.Core
11+ ( IDB
12+ , CursorDirection(..)
13+ , CursorSource(..)
14+ , Database
1015 , Index
1116 , KeyCursor
1217 , KeyRange
18+ , KeyPath
1319 , ObjectStore
1420 , Transaction
15− , ValueCursor
16− , CursorDirection(..)
17− , CursorSource(..)
18− , KeyPath
1921 , 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
22+ , ValueCursor
3123 , module Database.IndexedDB.IDBKey
3224 ) where
3325
34−import Prelude (class Show)
26+import Prelude (class Show)
3527
36−import Data.Maybe (Maybe(..))
37−import Data.String.Read (class Read)
28+import Control.Monad.Eff (kind Effect)
29+import Data.Maybe (Maybe(..))
30+import Data.String.Read (class Read)
3831
3932import Database.IndexedDB.IDBKey
4033
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
59−
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
34+-- | IDB Effects, manifestation that something happened with the IndexedDB
35+foreign import data IDB :: Effect
6636
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
7037
7138-- | A cursor has a direction that determines whether it moves in monotonically
7239-- | increasing or decreasing order of the record keys when iterated, and if it
@@ -79,6 +46,7 @@ data CursorDirection
7946 | Prev
8047 | PrevUnique
8148
49+
8250-- | If the source of a cursor is an object store, the effective object store of
8351-- | the cursor is that object store and the effective key of the cursor is the
8452-- | cursor’s position. If the source of a cursor is an index, the effective object
@@ -88,155 +56,134 @@ data CursorSource
8856 = ObjectStore ObjectStore
8957 | Index Index
9058
91−-- | A key path is a list of strings that defines how to extract a key from a value.
92−-- | A valid key path is one of:
93−-- |
94−-- | - An empty list.
95−-- | - An singleton identifier, which is a string matching the IdentifierName production from the ECMAScript Language Specification [ECMA-262].
96−-- | - A singleton string consisting of two or more identifiers separated by periods (U+002E FULL STOP).
97−-- | - A non-empty list containing only strings conforming to the above requirements.
98−type KeyPath = Array String
9959
10060-- | A transaction has a mode that determines which types of interactions can be performed
10161-- | upon that transaction. The mode is set when the transaction is created and remains
10262-- | fixed for the life of the transaction.
10363data 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.
107−
108−--------------------
109−-- Classes
110−--
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.
11167
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
11568
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
12069
121−-- | The IDBDatabase interface represents a connection to a database.
122−class IDBDatabase db
123−
124−-- | The IDBIndex interface represents an index handle.
125−-- | Any of these methods throw an "TransactionInactiveError" DOMException
126−-- | if called when the transaction is not active.
127−class IDBIndex index
70+-- | A key path is a list of strings that defines how to extract a key from a value.
71+-- | A valid key path is one of:
72+-- |
73+-- | - An empty list.
74+-- | - An singleton identifier, which is a string matching the IdentifierName production from the ECMAScript Language Specification [ECMA-262].
75+-- | - A singleton string consisting of two or more identifiers separated by periods (U+002E FULL STOP).
76+-- | - A non-empty list containing only strings conforming to the above requirements.
77+type KeyPath = Array String
12878
129−-- | The IDBKeyRange interface represents a key range.
130−class IDBKeyRange range
13179
132−-- | The IDBtransaction interface.
133−class IDBTransaction tx
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
13483
135−-- | The IDBObjectStore interface represents an object store handle.
136−class IDBObjectStore store
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
13787
138−--------------------
139−-- INSTANCES
140−--
14188
142−instance idbCursorKeyCursor :: IDBCursor KeyCursor
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
14392
144−instance idbCursorValueCursor :: IDBCursor ValueCursor
14593
146−instance idbConcreteCursorKeyCursor :: IDBConcreteCursor KeyCursor
94+-- | A key range is a continuous interval over some data type used for keys.
95+foreign import data KeyRange :: Type
14796
148−instance idbConcreteCursorValueCursor :: IDBConcreteCursor ValueCursor
14997
150−instance idbDatabaseDatabase :: IDBDatabase Database
98+-- | An object store is the primary storage mechanism for storing data in a database.
99+foreign import data ObjectStore :: Type
151100
152−instance idbIndexIndex :: IDBIndex Index
153101
154−instance idbIndexObjectStore :: IDBIndex ObjectStore
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
155105
156−instance idbKeyRangeKeyRange :: IDBKeyRange KeyRange
157106
158−instance idbObjectStoreObjectStore :: IDBObjectStore ObjectStore
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
159110
160−instance idbTransactionTransaction :: IDBTransaction Transaction
161111
112+foreign import _showCursor :: forall cursor. cursor -> String
162113instance showKeyCursor :: Show KeyCursor where
163114 show = _showCursor
164115
116+
165117instance showValueCursor :: Show ValueCursor where
166118 show = _showCursor
167119
120+
121+foreign import _showDatabase
122+ :: Database
123+ -> String
124+instance showDatabase :: Show Database where
125+ show = _showDatabase
126+
127+
128+foreign import _showIndex
129+ :: Index
130+ -> String
168131instance showIndex :: Show Index where
169132 show = _showIndex
170133
134+
135+foreign import _showKeyRange
136+ :: KeyRange
137+ -> String
171138instance showKeyRange :: Show KeyRange where
172139 show = _showKeyRange
173140
141+
142+foreign import _showObjectStore
143+ :: ObjectStore
144+ -> String
174145instance showObjectStore :: Show ObjectStore where
175146 show = _showObjectStore
176147
148+
149+foreign import _showTransaction
150+ :: Transaction
151+ -> String
177152instance showTransaction :: Show Transaction where
178153 show = _showTransaction
179154
180−instance showDatabase :: Show Database where
181− show = _showDatabase
182155
183156instance showCursorDirection :: Show CursorDirection where
184157 show x =
185158 case x of
186− Next -> "next"
159+ Next -> "next"
187160 NextUnique -> "nextunique"
188− Prev -> "prev"
161+ Prev -> "prev"
189162 PrevUnique -> "prevunique"
190163
164+
191165instance showTransactionMode :: Show TransactionMode where
192166 show x =
193167 case x of
194− ReadOnly -> "readonly"
195− ReadWrite -> "readwrite"
168+ ReadOnly -> "readonly"
169+ ReadWrite -> "readwrite"
196170 VersionChange -> "versionchange"
197171
172+
198173instance readCursorDirection :: Read CursorDirection where
199174 read s =
200175 case s of
201− "next" -> Just Next
176+ "next" -> Just Next
202177 "nextunique" -> Just NextUnique
203− "prev" -> Just Prev
178+ "prev" -> Just Prev
204179 "prevunique" -> Just PrevUnique
205− _ -> Nothing
180+ _ -> Nothing
181+
206182
207183instance readTransactionMode :: Read TransactionMode where
208184 read s =
209185 case s of
210− "readonly" -> Just ReadOnly
211− "readwrite" -> Just ReadWrite
186+ "readonly" -> Just ReadOnly
187+ "readwrite" -> Just ReadWrite
212188 "versionchange" -> Just VersionChange
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
189+ _ -> Nothing
src/Database/IndexedDB/IDBCursor.js+27−63
@@ -1,6 +1,6 @@
11const errorHandler = function errorHandler(cb) {
22 return function _handler(e) {
3− cb(e.target.error);
3+ cb(new Error(e.target.error.name));
44 };
55};
66
@@ -10,135 +10,99 @@ const successHandler = function successHandler(cb) {
1010 };
1111};
1212
13−export function _advance(cursor, count) {
14− return function aff(error, success) {
13+
14+exports._advance = function _advance(cursor, count) {
15+ return function aff(success, error) {
1516 try {
1617 cursor.advance(count);
1718 success();
1819 } catch (e) {
19− error(e);
20+ error(new Error(e.name));
2021 }
21−
22− return function canceler(_, cancelerError) {
23− cancelerError(new Error("Can't cancel IDB Effects"));
24− };
2522 };
2623};
2724
28−export function _continue(cursor, key) {
29− return function aff(error, success) {
25+exports._continue = function _continue(cursor, key) {
26+ return function aff(success, error) {
3027 try {
3128 cursor.continue(key || undefined);
3229 success();
3330 } catch (e) {
34− error(e);
31+ error(new Error(e.name));
3532 }
36−
37− return function canceler(_, cancelerError) {
38− cancelerError(new Error("Can't cancel IDB Effects"));
39− };
4033 };
4134};
4235
43−export function _continuePrimaryKey(cursor, key, primaryKey) {
44− return function aff(error, success) {
36+exports._continuePrimaryKey = function _continuePrimaryKey(cursor, key, primaryKey) {
37+ return function aff(success, error) {
4538 try {
4639 cursor.continuePrimaryKey(key, primaryKey);
4740 success();
4841 } catch (e) {
49− error(e);
42+ error(new Error(e.name));
5043 }
51−
52− return function canceler(_, cancelerError) {
53− cancelerError(new Error("Can't cancel IDB Effects"));
54− };
5544 };
5645};
5746
58−export function _delete(cursor) {
59− return function aff(error, success) {
47+exports._delete = function _delete(cursor) {
48+ return function aff(success, error) {
6049 try {
6150 const request = cursor.delete();
6251 request.onsuccess = successHandler(success);
6352 request.onerror = errorHandler(error);
6453 } catch (e) {
65− error(e);
54+ error(new Error(e.name));
6655 }
67−
68− return function canceler(_, cancelerError) {
69− cancelerError(new Error("Can't cancel IDB Effects"));
70− };
7156 };
7257};
7358
74−export function _direction(fromString, cursor) {
59+exports._direction = function _direction(fromString, cursor) {
7560 return fromString(cursor.direction);
7661};
7762
78−export function _key(cursor) {
79− return function aff(error, success) {
63+exports._key = function _key(cursor) {
64+ return function aff(success, error) {
8065 try {
8166 success(cursor.key);
8267 } catch (e) {
83− error(e);
68+ error(new Error(e.name));
8469 }
85−
86− return function canceler(_, cancelerError) {
87− cancelerError(new Error("Can't cancel IDB Effects"));
88− };
8970 };
9071};
9172
92−export function _primaryKey(cursor) {
93− return function aff(error, success) {
73+exports._primaryKey = function _primaryKey(cursor) {
74+ return function aff(success, error) {
9475 try {
9576 success(cursor.primaryKey);
9677 } catch (e) {
97− error(e);
78+ error(new Error(e.name));
9879 }
99−
100− return function canceler(_, cancelerError) {
101− cancelerError(new Error("Can't cancel IDB Effects"));
102− };
10380 };
10481};
10582
106−export function _source(IDBObjectStore, IDBIndex, cursor) {
83+exports._source = function _source(IDBObjectStore, IDBIndex, cursor) {
10784 switch (cursor.source.constructor.name) {
10885 case 'IDBIndex':
10986 return IDBIndex(cursor.source);
11087 case 'IDBObjectStore':
11188 return IDBObjectStore(cursor.source);
11289 default:
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− });
90+ throw new Error('UnexpectedCursorSource');
12391 }
12492};
12593
126−export function _update(cursor, value) {
127− return function aff(error, success) {
94+exports._update = function _update(cursor, value) {
95+ return function aff(success, error) {
12896 try {
12997 const request = cursor.update(value);
13098 request.onsuccess = successHandler(success);
13199 request.onerror = errorHandler(error);
132100 } catch (e) {
133− error(e);
101+ error(new Error(e.name));
134102 }
135−
136− return function canceler(_, cancelerError) {
137− cancelerError(new Error("Can't cancel IDB Effects"));
138− };
139103 };
140104};
141105
142−export function _value(cursor) {
106+exports._value = function _value(cursor) {
143107 return cursor.value;
144108};
src/Database/IndexedDB/IDBCursor.purs+162−110
@@ -1,186 +1,238 @@
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− -- * Interface
5− ( advance
6− , continue
7− , continuePrimaryKey
8− , delete
9− , update
10−
11− -- * Attributes
4+ ( class IDBCursor, advance, continue, continuePrimaryKey, delete, update
125 , direction
136 , key
147 , primaryKey
158 , source
9+ , direction'
10+ , key'
11+ , primaryKey'
12+ , source'
1613 , value
1714 ) where
1815
16+import Prelude (Unit, ($), (>>>), map)
17+
18+import Control.Monad.Aff (Aff)
19+import Data.Foreign (Foreign, toForeign, unsafeFromForeign)
20+import Data.Function.Uncurried as Fn
21+import Data.Function.Uncurried (Fn2, Fn3)
22+import Data.Maybe (Maybe)
23+import Data.Nullable (Nullable, toNullable)
24+import Data.String.Read (read)
25+
1926import Database.IndexedDB.Core
27+import Database.IndexedDB.IDBKey.Internal (class IDBKey, Key(..), toKey, extractForeign)
2028
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)
3129
3230--------------------
3331-- INTERFACES
3432--
33+-- | Cursor objects implement the IDBCursor interface.
34+-- | There is only ever one IDBCursor instance representing a given cursor.
35+-- | There is no limit on how many cursors can be used at the same time.
36+class IDBCursor cursor where
37+ -- | Advances the cursor through the next count records in range.
38+ advance
39+ :: forall e
40+ . cursor
41+ -> Int
42+ -> Aff (idb :: IDB | e) Unit
43+
44+ -- | Advances the cursor to the next record in range matching or after key.
45+ continue
46+ :: forall e k. (IDBKey k)
47+ => cursor
48+ -> Maybe k
49+ -> Aff (idb :: IDB | e) Unit
50+
51+ -- | 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.
52+ continuePrimaryKey
53+ :: forall e k. (IDBKey k)
54+ => cursor
55+ -> k
56+ -> k
57+ -> Aff (idb :: IDB | e) Unit
58+
59+ -- | Delete the record pointed at by the cursor with a new value.
60+ delete
61+ :: forall e
62+ . cursor
63+ -> Aff (idb :: IDB | e) Unit
64+
65+ -- | Update the record pointed at by the cursor with a new value.
66+ -- |
67+ -- | Throws a "DataError" DOMException if the effective object store uses
68+ -- | in-line keys and the key would have changed.
69+ update
70+ :: forall val e
71+ . cursor
72+ -> val
73+ -> Aff (idb :: IDB | e) Key
3574
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
9075
9176--------------------
9277-- ATTRIBUTES
9378--
79+-- | Returns the direction (Next|NextUnique|Prev|PrevUnique) of the cursor.
80+direction'
81+ :: KeyCursor
82+ -> CursorDirection
83+direction' =
84+ Fn.runFn2 _direction (read >>> toNullable)
85+
86+
87+-- | Returns the key of the cursor. Throws a "InvalidStateError" DOMException
88+-- | if the cursor is advancing or is finished.
89+key'
90+ :: forall e
91+ . KeyCursor
92+ -> Aff (idb :: IDB | e) Key
93+key' =
94+ _key >>> map Key
95+
96+
97+-- | Returns the effective key of the cursor. Throws a "InvalidStateError" DOMException
98+-- | if the cursor is advancing or is finished.
99+primaryKey'
100+ :: forall e
101+ . KeyCursor
102+ -> Aff (idb :: IDB | e) Key
103+primaryKey' =
104+ _primaryKey >>> map Key
105+
106+
107+-- | Returns the IDBObjectStore or IDBIndex the cursor was opened from.
108+source'
109+ :: KeyCursor
110+ -> CursorSource
111+source' =
112+ Fn.runFn3 _source ObjectStore Index
113+
114+
115+-- NOTE: Polymorphism not used here cause *we want* those methods to be distincs for
116+-- the concrete types mostly for consistency with the Database.IndexedDB.IDBIndex module
117+-- where the IDBIndex interfaces is shared by Index and ObjectStore, but attributes of
118+-- respective concrete types aren't shared.
119+
94120
95121-- | Returns the direction (Next|NextUnique|Prev|PrevUnique) of the cursor.
96122direction
97− :: forall cursor
98− . (IDBConcreteCursor cursor)
99− => cursor
123+ :: ValueCursor
100124 -> CursorDirection
101125direction =
102126 Fn.runFn2 _direction (read >>> toNullable)
103127
128+
104129-- | Returns the key of the cursor. Throws a "InvalidStateError" DOMException
105130-- | if the cursor is advancing or is finished.
106131key
107− :: forall cursor
108− . (IDBConcreteCursor cursor)
109− => cursor
110− -> Aff Key
132+ :: forall e
133+ . ValueCursor
134+ -> Aff (idb :: IDB | e) Key
111135key =
112− map toKey <<< fromEffectFnAff <<< _key
136+ _key >>> map Key
137+
113138
114139-- | Returns the effective key of the cursor. Throws a "InvalidStateError" DOMException
115140-- | if the cursor is advancing or is finished.
116141primaryKey
117− :: forall cursor
118− . (IDBConcreteCursor cursor)
119− => cursor
120− -> Aff Key
142+ :: forall e
143+ . ValueCursor
144+ -> Aff (idb :: IDB | e) Key
121145primaryKey =
122− map toKey <<< fromEffectFnAff <<< _primaryKey
146+ _primaryKey >>> map Key
147+
123148
124149-- | Returns the IDBObjectStore or IDBIndex the cursor was opened from.
125150source
126− :: forall cursor
127− . (IDBConcreteCursor cursor)
128− => cursor
151+ :: ValueCursor
129152 -> CursorSource
130153source =
131154 Fn.runFn3 _source ObjectStore Index
132155
156+
133157value
134158 :: forall val
135− . ValueCursor
159+ . ValueCursor
136160 -> val
137161value =
138162 _value >>> unsafeFromForeign
139163
164+
140165--------------------
141−-- FFI
166+-- INSTANCES
142167--
168+instance keyCursorKeyCursor :: IDBCursor KeyCursor where
169+ advance = Fn.runFn2 _advance
170+ continue c mk = Fn.runFn2 _continue c (toNullable $ map (toKey >>> extractForeign) mk)
171+ continuePrimaryKey c k1 k2 = Fn.runFn3 _continuePrimaryKey c (extractForeign $ toKey k1) (extractForeign $ toKey k2)
172+ delete = _delete
173+ update c = toForeign >>> Fn.runFn2 _update c >>> map Key
174+
143175
176+instance valueCursorKeyCursor :: IDBCursor ValueCursor where
177+ advance = Fn.runFn2 _advance
178+ continue c mk = Fn.runFn2 _continue c (toNullable $ map (toKey >>> extractForeign) mk)
179+ continuePrimaryKey c k1 k2 = Fn.runFn3 _continuePrimaryKey c (extractForeign $ toKey k1) (extractForeign $ toKey k2)
180+ delete = _delete
181+ update c = toForeign >>> Fn.runFn2 _update c >>> map Key
182+
183+
184+--------------------
185+-- FFI
186+--
144187foreign import _advance
145− :: forall cursor
146− . Fn2 cursor Int (EffectFnAff Unit)
188+ :: forall cursor e
189+ . Fn2 cursor Int (Aff (idb :: IDB | e) Unit)
190+
147191
148192foreign import _continue
149− :: forall cursor
150− . Fn2 cursor (Nullable Foreign) (EffectFnAff Unit)
193+ :: forall cursor e
194+ . Fn2 cursor (Nullable Foreign) (Aff (idb :: IDB | e) Unit)
195+
151196
152197foreign import _continuePrimaryKey
153− :: forall cursor
154− . Fn3 cursor Foreign Foreign (EffectFnAff Unit)
198+ :: forall cursor e
199+ . Fn3 cursor Foreign Foreign (Aff (idb :: IDB | e) Unit)
200+
155201
156202foreign import _delete
157− :: forall cursor
158− . cursor
159− -> (EffectFnAff Unit)
203+ :: forall cursor e
204+ . cursor
205+ -> (Aff (idb :: IDB | e) Unit)
206+
160207
161208foreign import _direction
162209 :: forall cursor
163− . Fn2 (String -> Nullable CursorDirection) cursor CursorDirection
210+ . Fn2 (String -> Nullable CursorDirection) cursor CursorDirection
211+
164212
165213foreign import _key
166− :: forall cursor
167− . cursor
168− -> EffectFnAff Key
214+ :: forall cursor e
215+ . cursor
216+ -> Aff (idb :: IDB | e) Foreign
217+
169218
170219foreign import _primaryKey
171− :: forall cursor
172− . cursor
173− -> EffectFnAff Key
220+ :: forall cursor e
221+ . cursor
222+ -> Aff (idb :: IDB | e) Foreign
223+
174224
175225foreign import _source
176226 :: forall cursor
177− . Fn3 (ObjectStore -> CursorSource) (Index -> CursorSource) cursor CursorSource
227+ . Fn3 (ObjectStore -> CursorSource) (Index -> CursorSource) cursor CursorSource
228+
178229
179230foreign import _update
180− :: forall cursor
181− . Fn2 cursor Foreign (EffectFnAff Foreign)
231+ :: forall cursor e
232+ . Fn2 cursor Foreign (Aff (idb :: IDB | e) Foreign)
233+
182234
183235foreign import _value
184236 :: forall cursor val
185− . cursor
237+ . cursor
186238 -> val
src/Database/IndexedDB/IDBDatabase.js+28−62
@@ -1,25 +1,21 @@
1−const toArray = function toArray(xs) {
2− return Array.prototype.slice.apply(xs);
3−};
1+const $Core = require('Database.IndexedDB.Core/foreign');
2+
3+const toArray = $Core.toArray;
44
55
6−export function _close(db) {
7− return function aff(error, success) {
6+exports._close = function _close(db) {
7+ return function aff(success, error) {
88 try {
99 db.close();
1010 success();
1111 } catch (e) {
12− error(e);
12+ error(new Error(e.name));
1313 }
14−
15− return function canceler(_, cancelerError) {
16− cancelerError(new Error("Can't cancel IDB Effects"));
17− };
1814 };
1915};
2016
21−export function _createObjectStore(db, name, opts) {
22− return function aff(error, success) {
17+exports._createObjectStore = function _createObjectStore(db, name, opts) {
18+ return function aff(success, error) {
2319 var keyPath;
2420
2521 try {
@@ -43,107 +39,77 @@ export function _createObjectStore(db, name, opts) {
4339 });
4440 success(store);
4541 } catch (e) {
46− error(e);
42+ error(new Error(e.name));
4743 }
48−
49− return function canceler(_, cancelerError) {
50− cancelerError(new Error("Can't cancel IDB Effects"));
51− };
5244 };
5345};
5446
55−export function _deleteObjectStore(db, name) {
56− return function aff(error, success) {
47+exports._deleteObjectStore = function _deleteObjectStore(db, name) {
48+ return function aff(success, error) {
5749 try {
5850 db.deleteObjectStore(name);
5951 success();
6052 } catch (e) {
61− error(e);
53+ error(new Error(e.name));
6254 }
63−
64− return function canceler(_, cancelerError) {
65− cancelerError(new Error("Can't cancel IDB Effects"));
66− };
6755 };
6856};
6957
70−export function _name(db) {
58+exports._name = function _name(db) {
7159 return db.name;
7260};
7361
74−export function _objectStoreNames(db) {
62+exports._objectStoreNames = function _objectStoreNames(db) {
7563 return toArray(db.objectStoreNames);
7664};
7765
78−export function _onAbort(db, f) {
79− return function aff(error, success) {
66+exports._onAbort = function _onAbort(db, f) {
67+ return function aff(success) {
8068 db.onabort = function onabort() {
8169 f();
8270 };
8371 success();
84−
85− return function canceler(_, cancelerError) {
86− cancelerError(new Error("Can't cancel IDB Effects"));
87− };
8872 };
8973};
9074
91−export function _onClose(db, f) {
92− return function aff(error, success) {
75+exports._onClose = function _onClose(db, f) {
76+ return function aff(success) {
9377 db.onclose = function onclose() {
9478 f();
9579 };
9680 success();
97−
98− return function canceler(_, cancelerError) {
99− cancelerError(new Error("Can't cancel IDB Effects"));
100− };
10181 };
10282};
10383
104−export function _onError(db, f) {
105− return function aff(error, success) {
84+exports._onError = function _onError(db, f) {
85+ return function aff(success) {
10686 db.onerror = function onerror(e) {
107− f(e.target.error)();
87+ f(new Error(e.target.error.name))();
10888 };
10989 success();
110−
111− return function canceler(_, cancelerError) {
112− cancelerError(new Error("Can't cancel IDB Effects"));
113− };
11490 };
11591};
11692
117−export function _onVersionChange(db, f) {
118− return function aff(error, success) {
93+exports._onVersionChange = function _onVersionChange(db, f) {
94+ return function aff(success) {
11995 db.onversionchange = function onversionchange(e) {
12096 f({ oldVersion: e.oldVersion, newVersion: e.newVersion })();
12197 };
12298 success();
123−
124− return function canceler(_, cancelerError) {
125− cancelerError(new Error("Can't cancel IDB Effects"));
126− };
12799 };
128100};
129101
130−export function _transaction(db, stores, mode) {
131− return function aff(error, success) {
132− var transaction;
102+exports._transaction = function _transaction(db, stores, mode) {
103+ return function aff(success, error) {
133104 try {
134− transaction = db.transaction(stores, mode);
105+ const transaction = db.transaction(stores, mode);
135106 success(transaction);
136107 } catch (e) {
137− error(e);
108+ error(new Error(e.name));
138109 }
139−
140− return function canceler(_, cancelerError, cancelerSuccess) {
141− transaction.abort();
142− cancelerSuccess();
143− };
144110 };
145111};
146112
147−export function _version(db) {
113+exports._version = function _version(db) {
148114 return db.version;
149115};
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− -- * Types
5− ( StoreName
6− , ObjectStoreParameters
7− , defaultParameters
8−
9− -- * Interface
10− , close
11− , createObjectStore
12− , deleteObjectStore
13− , transaction
14−
15− -- * Attributes
4+ ( class IDBDatabase, close, createObjectStore, deleteObjectStore, transaction
5+ , StoreName
166 , name
177 , objectStoreNames
188 , version
19−
20− -- * Event handlers
219 , onAbort
2210 , onClose
2311 , onError
2412 , onVersionChange
2513 ) where
2614
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+
2723import Database.IndexedDB.Core
24+import Database.IndexedDB.IDBObjectStore (IDBObjectStoreParameters)
2825
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, (<<<), ($))
3626
3727--------------------
38−-- TYPES
28+-- INTERFACE
3929--
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+
4066
4167-- | Type alias for StoreName
4268type StoreName = String
4369
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')
10570
10671--------------------
10772-- ATTRIBUTES
10873--
109−
11074-- | Returns the name of the database.
11175name
112− :: Database
113− -> String
76+ :: Database
77+ -> String
11478name =
11579 _name
11680
81+
11782-- | Returns a list of the names of object stores in the database.
11883objectStoreNames
119− :: Database
120− -> Array String
84+ :: Database
85+ -> Array String
12186objectStoreNames =
12287 _objectStoreNames
12388
89+
12490-- | Returns the version of the database.
12591version
126− :: Database
127− -> Int
92+ :: Database
93+ -> Int
12894version =
12995 _version
13096
97+
13198--------------------
13299-- EVENT HANDLERS
133100--
134101-- | Event handler for the `abort` event.
135102onAbort
136− :: Database
137− -> Effect Unit
138− -> Aff Unit
103+ :: forall e e'
104+ . Database
105+ -> Eff ( | e') Unit
106+ -> Aff (idb :: IDB | e) Unit
139107onAbort db f =
140− fromEffectFnAff $ Fn.runFn2 _onAbort db f
108+ Fn.runFn2 _onAbort db f
109+
141110
142111-- | Event handler for the `close` event.
143112onClose
144− :: Database
145− -> Effect Unit
146− -> Aff Unit
113+ :: forall e e'
114+ . Database
115+ -> Eff ( | e') Unit
116+ -> Aff (idb :: IDB | e) Unit
147117onClose db f =
148− fromEffectFnAff $ Fn.runFn2 _onClose db f
118+ Fn.runFn2 _onClose db f
119+
149120
150121-- | Event handler for the `error` event.
151122onError
152− :: Database
153− -> (Error -> Effect Unit)
154− -> Aff Unit
123+ :: forall e e'
124+ . Database
125+ -> (Error -> Eff ( | e') Unit)
126+ -> Aff (idb :: IDB | e) Unit
155127onError db f =
156− fromEffectFnAff $ Fn.runFn2 _onError db f
128+ Fn.runFn2 _onError db f
129+
157130
158131-- | Event handler for the `versionchange` event.
159132onVersionChange
160− :: Database
161− -> ( { oldVersion :: Int, newVersion :: Int }
162− -> Effect Unit
163− )
164− -> Aff Unit
133+ :: forall e e'
134+ . Database
135+ -> ({ oldVersion :: Int, newVersion :: Int }
136+ -> Eff ( | e') Unit)
137+ -> Aff (idb :: IDB | e) Unit
165138onVersionChange db f =
166− fromEffectFnAff $ Fn.runFn2 _onVersionChange db f
139+ Fn.runFn2 _onVersionChange db f
140+
167141
168142--------------------
169−-- FFI
143+-- INSTANCES
170144--
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+
171158
159+--------------------
160+-- FFI
161+--
172162foreign import _close
173− :: forall db
174− . db
175− -> EffectFnAff Unit
163+ :: forall db e
164+ . db
165+ -> Aff (idb :: IDB | e) Unit
166+
176167
177168foreign import _createObjectStore
178− :: forall db
179− . Fn3 db String { keyPath :: Array String, autoIncrement :: Boolean } (EffectFnAff ObjectStore)
169+ :: forall db e
170+ . Fn3 db String { keyPath :: Array String, autoIncrement :: Boolean } (Aff (idb :: IDB | e) ObjectStore)
171+
180172
181173foreign import _deleteObjectStore
182− :: forall db
183− . Fn2 db String (EffectFnAff ObjectStore)
174+ :: forall db e
175+ . Fn2 db String (Aff (idb :: IDB | e) ObjectStore)
176+
184177
185178foreign import _name
186− :: Database
187− -> String
179+ :: Database
180+ -> String
181+
188182
189183foreign import _objectStoreNames
190− :: Database
191− -> Array String
184+ :: Database
185+ -> Array String
186+
192187
193188foreign import _onAbort
194− :: forall db
195− . Fn2 db (Effect Unit) (EffectFnAff Unit)
189+ :: forall db e e'
190+ . Fn2 db (Eff ( | e') Unit) (Aff (idb :: IDB | e) Unit)
191+
196192
197193foreign import _onClose
198− :: forall db
199− . Fn2 db (Effect Unit) (EffectFnAff Unit)
194+ :: forall db e e'
195+ . Fn2 db (Eff ( | e') Unit) (Aff (idb :: IDB | e) Unit)
196+
200197
201198foreign import _onError
202− :: forall db
203− . Fn2 db (Error -> Effect Unit) (EffectFnAff Unit)
199+ :: forall db e e'
200+ . Fn2 db (Error -> Eff ( | e') Unit) (Aff (idb :: IDB | e) Unit)
201+
204202
205203foreign import _onVersionChange
206− :: forall db
207− . Fn2 db ({ oldVersion :: Int, newVersion :: Int } -> Effect Unit) (EffectFnAff Unit)
204+ :: forall db e e'
205+ . Fn2 db ({ oldVersion :: Int, newVersion :: Int } -> Eff ( | e') Unit) (Aff (idb :: IDB | e) Unit)
206+
208207
209208foreign import _transaction
210− :: forall db
211− . Fn3 db (Array String) String (EffectFnAff Transaction)
209+ :: forall db e
210+ . Fn3 db (Array String) String (Aff (idb :: IDB | e) Transaction)
211+
212212
213213foreign import _version
214− :: Database
215− -> Int
214+ :: Database
215+ -> Int
src/Database/IndexedDB/IDBFactory.js+8−21
@@ -1,6 +1,6 @@
11const errorHandler = function errorHandler(cb) {
22 return function _handler(e) {
3− cb(e.target.error);
3+ cb(new Error(e.target.error.name));
44 };
55};
66
@@ -14,12 +14,9 @@ const noOp2 = function noOp2() {
1414 return noOp;
1515};
1616
17−const noOp3 = function noOp3() {
18− return noOp2;
19−};
2017
21−export function _deleteDatabase(name) {
22− return function aff(error, success) {
18+exports._deleteDatabase = function _deleteDatabase(name) {
19+ return function aff(success, error) {
2320 try {
2421 const request = indexedDB.deleteDatabase(name);
2522
@@ -29,19 +26,15 @@ export function _deleteDatabase(name) {
2926
3027 request.onerror = errorHandler(error);
3128 } catch (e) {
32− error(e);
29+ error(new Error(e.name));
3330 }
34−
35− return function canceler(_, cancelerError) {
36− cancelerError(new Error("Can't cancel IDB Effects"));
37− };
3831 };
3932};
4033
41−export function _open(fromMaybe, name, mver, req) {
34+exports._open = function _open(fromMaybe, name, mver, req) {
4235 const ver = fromMaybe(undefined)(mver);
4336
44− return function aff(error, success) {
37+ return function aff(success, error) {
4538 try {
4639 const request = indexedDB.open(name, ver);
4740 request.onsuccess = function onSuccess(e) {
@@ -53,18 +46,12 @@ export function _open(fromMaybe, name, mver, req) {
5346 };
5447
5548 request.onupgradeneeded = function onUpgradeNeeded(e) {
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)();
49+ fromMaybe(noOp2)(req.onUpgradeNeeded)(e.target.result)(e.target.transaction)();
5950 };
6051
6152 request.onerror = errorHandler(error);
6253 } catch (e) {
63− error(e);
54+ error(new Error(e.name));
6455 }
65−
66− return function canceler(_, cancelerError) {
67− cancelerError(new Error("Can't cancel IDB Effects"));
68− };
6956 };
7057};
src/Database/IndexedDB/IDBFactory.purs+32−36
@@ -1,55 +1,47 @@
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
5− -- * Types
6− ( Callbacks
7− , DatabaseName
8− , Version
4+module Database.IndexedDB.IDBFactory where
95
10− -- * Interface
11− , deleteDatabase
12− , open
13− ) where
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)
1413
1514import Database.IndexedDB.Core
1615
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, ($), (<<<))
2416
2517--------------------
26−-- TYPES
18+-- INTERFACE
2719--
28−
2920-- Type alias for binding listeners to an initial open action.
30−type Callbacks =
31− { onBlocked :: Maybe (Effect Unit)
32− , onUpgradeNeeded :: Maybe (Database -> Transaction -> { oldVersion :: Int } -> Effect Unit)
21+type Callbacks e =
22+ { onBlocked :: Maybe (Eff (| e) Unit)
23+ , onUpgradeNeeded :: Maybe (Database -> Transaction -> Eff (| e) Unit)
3324 }
3425
26+
3527-- | Type alias for DatabaseName.
3628type DatabaseName = String
3729
30+
3831-- | Type alias for Version.
3932type Version = Int
4033
41−--------------------
42−-- INTERFACE
43−--
4434
4535-- | Attempts to delete the named database. If the database already exists
4636-- | and there are open connections that don’t close in response to a
4737-- | `versionchange` event, the request will be blocked until all they close.
4838deleteDatabase
49− :: DatabaseName
50− -> Aff Int
39+ :: forall e
40+ . DatabaseName
41+ -> Aff (idb :: IDB | e) Int
5142deleteDatabase =
52− fromEffectFnAff <<< _deleteDatabase
43+ _deleteDatabase
44+
5345
5446-- | Attempts to open a connection to the named database with the specified version.
5547-- | If the database already exists with a lower version and there are open connections
@@ -60,20 +52,24 @@ deleteDatabase =
6052-- | When the version isn't provided (`Nothing`), attempts to open a connection to the
6153-- | named database with the current version, or 1 if it does not already exist.
6254open
63− :: DatabaseName
64− -> Maybe Version
65− -> Callbacks
66− -> Aff Database
55+ :: forall e e'
56+ . DatabaseName
57+ -> Maybe Version
58+ -> Callbacks e'
59+ -> Aff (idb :: IDB | e) Database
6760open name mver req =
68− fromEffectFnAff $ Fn.runFn4 _open fromMaybe name mver req
61+ Fn.runFn4 _open fromMaybe name mver req
62+
6963
7064--------------------
7165-- FFI
7266--
7367foreign import _deleteDatabase
74− :: String
75− -> EffectFnAff Int
68+ :: forall e
69+ . String
70+ -> Aff (idb :: IDB | e) Int
71+
7672
7773foreign import _open
78− :: forall a
79− . Fn4 (a -> Maybe a -> a) String (Maybe Int) (Callbacks) (EffectFnAff Database)
74+ :: forall a e e'
75+ . Fn4 (a -> Maybe a -> a) String (Maybe Int) (Callbacks e') (Aff (idb :: IDB | e) Database)
src/Database/IndexedDB/IDBIndex.purs+7−197
@@ -1,206 +1,16 @@
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− -- * Types
5− ( Callbacks
6−
7− -- * Interface
8− , count
9− , get
10− , getAllKeys
11− , getKey
12− , openCursor
13− , openKeyCursor
4+ ( module Database.IndexedDB.IDBIndex.Internal
5+ ) where
146
15− -- * Attributes
7+import Database.IndexedDB.IDBIndex.Internal
8+ ( class IDBIndex, count, get, getAllKeys, getKey, openCursor, openKeyCursor
9+ , IDBIndexParameters
1610 , keyPath
1711 , multiEntry
1812 , name
1913 , objectStore
2014 , unique
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)
15+ , defaultParameters
16+ )
src/Database/IndexedDB/IDBIndex.js → src/Database/IndexedDB/IDBIndex/Internal.jsrenamed+28−52
@@ -1,6 +1,6 @@
11const errorHandler = function errorHandler(cb) {
22 return function _handler(e) {
3− cb(e.target.error);
3+ cb(new Error(e.target.error.name));
44 };
55};
66
@@ -11,7 +11,7 @@ const successHandler = function successHandler(cb) {
1111};
1212
1313
14−export function _keyPath(index) {
14+exports._keyPath = function _keyPath(index) {
1515 const path = index.keyPath;
1616
1717 if (Array.isArray(path)) {
@@ -25,51 +25,43 @@ export function _keyPath(index) {
2525 return [];
2626};
2727
28−export function _multiEntry(index) {
28+exports._multiEntry = function _multiEntry(index) {
2929 return index.multiEntry;
3030};
3131
32−export function _name(index) {
32+exports._name = function _name(index) {
3333 return index.name;
3434};
3535
36−export function _objectStore(index) {
36+exports._objectStore = function _objectStore(index) {
3737 return index.obectStore;
3838};
3939
40−export function _unique(index) {
40+exports._unique = function _unique(index) {
4141 return index.unique;
4242};
4343
44−export function _count(index, query) {
45− return function aff(error, success) {
44+exports._count = function _count(index, query) {
45+ return function aff(success, error) {
4646 try {
4747 const request = index.count(query);
4848 request.onsuccess = successHandler(success);
4949 request.onerror = errorHandler(error);
5050 } catch (e) {
51− error(e);
51+ error(new Error(e.name));
5252 }
53−
54− return function canceler(_, cancelerError) {
55− cancelerError(new Error("Can't cancel IDB Effects"));
56− };
5753 };
5854};
5955
60−export function _get(index, range) {
61− return function aff(error, success) {
56+exports._get = function _get(index, range) {
57+ return function aff(success, error) {
6258 try {
6359 const request = index.get(range);
6460 request.onsuccess = successHandler(success);
6561 request.onerror = errorHandler(error);
6662 } catch (e) {
67− error(e);
63+ error(new Error(e.name));
6864 }
69−
70− return function canceler(_, cancelerError) {
71− cancelerError(new Error("Can't cancel IDB Effects"));
72− };
7365 };
7466};
7567
@@ -79,8 +71,8 @@ export function _get(index, range) {
7971 *
8072 * However, it may be doable to convert the result to some key / value structure with values of
8173 * different types.
82−export function _getAll(index, query, count) {
83− return function aff(error, success) {
74+exports._getAll = function _getAll(index, query, count) {
75+ return function aff(success, error) {
8476 const request = index.getAll(query, count);
8577 request.onsuccess = successHandler(success);
8678 request.onerror = errorHandler(error);
@@ -88,40 +80,32 @@ export function _getAll(index, query, count) {
8880};
8981*/
9082
91−export function _getAllKeys(index, range, count) {
92− return function aff(error, success) {
83+exports._getAllKeys = function _getAllKeys(index, range, count) {
84+ return function aff(success, error) {
9385 try {
9486 const request = index.getAllKeys(range, count || undefined);
9587 request.onsuccess = successHandler(success);
9688 request.onerror = errorHandler(error);
9789 } catch (e) {
98− error(e);
90+ error(new Error(e.name));
9991 }
100−
101− return function canceler(_, cancelerError) {
102− cancelerError(new Error("Can't cancel IDB Effects"));
103− };
10492 };
10593};
10694
107−export function _getKey(index, range) {
108− return function aff(error, success) {
95+exports._getKey = function _getKey(index, range) {
96+ return function aff(success, error) {
10997 try {
11098 const request = index.getKey(range);
11199 request.onsuccess = successHandler(success);
112100 request.onerror = errorHandler(error);
113101 } catch (e) {
114− error(e);
102+ error(new Error(e.name));
115103 }
116−
117− return function canceler(_, cancelerError) {
118− cancelerError(new Error("Can't cancel IDB Effects"));
119− };
120104 };
121105};
122106
123−export function _openCursor(index, query, dir, cb) {
124− return function aff(error, success) {
107+exports._openCursor = function _openCursor(index, query, dir, cb) {
108+ return function aff(success, error) {
125109 try {
126110 const request = index.openCursor(query, dir);
127111 request.onsuccess = function onSuccess(e) {
@@ -132,21 +116,17 @@ export function _openCursor(index, query, dir, cb) {
132116 }
133117 };
134118 request.onerror = function onError(e) {
135− cb.onError(e.target.error);
119+ cb.onError(new Error(e.target.error.name));
136120 };
137121 success();
138122 } catch (e) {
139− error(e);
123+ error(new Error(e.name));
140124 }
141−
142− return function canceler(_, cancelerError) {
143− cancelerError(new Error("Can't cancel IDB Effects"));
144− };
145125 };
146126};
147127
148−export function _openKeyCursor(index, query, dir, cb) {
149− return function aff(error, success) {
128+exports._openKeyCursor = function _openKeyCursor(index, query, dir, cb) {
129+ return function aff(success, error) {
150130 try {
151131 const request = index.openKeyCursor(query, dir);
152132 request.onsuccess = function onSuccess(e) {
@@ -157,15 +137,11 @@ export function _openKeyCursor(index, query, dir, cb) {
157137 }
158138 };
159139 request.onerror = function onError(e) {
160− cb.onError(e.target.error);
140+ cb.onError(new Error(e.target.error.name));
161141 };
162142 success();
163143 } catch (e) {
164− error(e);
144+ error(new Error(e.name));
165145 }
166−
167− return function canceler(_, cancelerError) {
168− cancelerError(new Error("Can't cancel IDB Effects"));
169− };
170146 };
171147};
src/Database/IndexedDB/IDBIndex/Internal.pursnew file+256
@@ -0,0 +1,256 @@
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, KeyPath, ObjectStore, ValueCursor)
17+import Database.IndexedDB.IDBKey.Internal (class IDBKey, Key(..))
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+ map Key <$> Fn.runFn3 _getAllKeys index (toNullable range) (toNullable count)
170+
171+ getKey index range =
172+ (toMaybe >>> map Key) <$> 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+ map Key <$> Fn.runFn3 _getAllKeys store (toNullable range) (toNullable count)
190+
191+ getKey store range =
192+ (toMaybe >>> map Key) <$> 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 Foreign))
242+
243+
244+foreign import _getKey
245+ :: forall index e
246+ . Fn2 index KeyRange (Aff (idb :: IDB | e) (Nullable Foreign))
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+1−8
@@ -5,11 +5,4 @@ module Database.IndexedDB.IDBKey
55 ( module Database.IndexedDB.IDBKey.Internal
66 ) where
77
8−import Database.IndexedDB.IDBKey.Internal
9− ( class IDBKey
10− , Key
11− , none
12− , toKey
13− , fromKey
14− , unsafeFromKey
15− )
8+import Database.IndexedDB.IDBKey.Internal(class IDBKey, Key, none, toKey, fromKey, unsafeFromKey)
src/Database/IndexedDB/IDBKey/Internal.js+3−3
@@ -1,8 +1,8 @@
1−export function _dateTimeToForeign(y, m, d, h, mi, s, ms) {
1+exports._dateTimeToForeign = function _dateTimeToForeign(y, m, d, h, mi, s, ms) {
22 return new Date(y, m, d, h, mi, s, ms);
33};
44
5−export function _readDateTime(parse, right, left, date) {
5+exports._readDateTime = function _readDateTime(parse, right, left, date) {
66 if (Object.getPrototypeOf(date) !== Date.prototype) {
77 return left(typeof date);
88 }
@@ -24,7 +24,7 @@ export function _readDateTime(parse, right, left, date) {
2424 return right(mdate);
2525};
2626
27−export function _unsafeReadDateTime(parse, date) {
27+exports._unsafeReadDateTime = function _unsafeReadDateTime(parse, date) {
2828 const y = date.getFullYear();
2929 const m = date.getMonth() + 1;
3030 const d = date.getDate();
src/Database/IndexedDB/IDBKey/Internal.purs+114−96
@@ -2,37 +2,44 @@
22-- |
33-- | NOTE: Binary keys aren't supported yet.
44module Database.IndexedDB.IDBKey.Internal
5− ( Key
6− , class IDBKey
7− , toKey
8− , fromKey
9− , unsafeFromKey
10− , none
11− ) where
5+ ( Key(..)
6+ , none
7+ , class IDBKey, toKey , fromKey , unsafeFromKey
8+ , extractForeign
9+ ) where
1210
1311import Prelude
1412
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)
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+
3332
3433newtype Key = Key Foreign
3534
35+
36+extractForeign
37+ :: Key
38+ -> Foreign
39+extractForeign (Key f) =
40+ f
41+
42+
3643--------------------
3744-- INTERFACES
3845--
@@ -40,90 +47,94 @@ newtype Key = Key Foreign
4047-- | to a known type (e.g if you only strings as keys, or perfectly knows the
4148-- | type of a given key).
4249class IDBKey a where
43− toKey :: a -> Key
44− fromKey :: Key -> F a
50+ toKey :: a -> Key
51+ fromKey :: Key -> F a
4552 unsafeFromKey :: Key -> a
4653
54+
4755none :: Maybe Key
4856none =
4957 Nothing
5058
59+
5160--------------------
5261-- INSTANCES
5362--
5463instance eqKey :: Eq Key where
5564 eq a b = (runExceptT >>> runIdentity >>> isRight) $
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
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
6574 where
66− runIdentity :: forall a. Identity a -> a
67− runIdentity (Identity x) = x
75+ runIdentity :: forall a. Identity a -> a
76+ runIdentity (Identity x) = x
77+
6878
6979instance ordKey :: Ord Key where
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
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
8090 where
81− runIdentity :: forall a. Identity a -> a
82− runIdentity (Identity x) = x
91+ runIdentity :: forall a. Identity a -> a
92+ runIdentity (Identity x) = x
93+
8394
8495instance showKey :: Show Key where
8596 show a = (runExceptT >>> format) $
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)))
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)))
95106 where
96− format :: forall a. Identity (Either a String) -> String
97− format (Identity x) =
98− either (const "(Key)") (\s -> "(Key " <> s <> ")") x
107+ format :: forall a. Identity (Either a String) -> String
108+ format (Identity x) =
109+ either (const "(Key)") (\s -> "(Key " <> s <> ")") x
110+
99111
100112instance idbKeyKey :: IDBKey Key where
101− toKey = identity
102− fromKey = pure
103− unsafeFromKey = identity
113+ toKey = id
114+ fromKey = pure
115+ unsafeFromKey = id
104116
105−instance idbKeyForeign :: IDBKey Foreign where
106− toKey = Key
107− fromKey (Key f) = pure f
108− unsafeFromKey (Key f) = f
109117
110118instance idbKeyInt :: IDBKey Int where
111− toKey = Foreign.unsafeToForeign >>> Key
112− fromKey (Key f) = Foreign.readInt f
119+ toKey = Foreign.toForeign >>> Key
120+ fromKey (Key f) = Foreign.readInt f
113121 unsafeFromKey (Key f) = Foreign.unsafeFromForeign f
114122
123+
115124instance idbKeyNumber :: IDBKey Number where
116− toKey = Foreign.unsafeToForeign >>> Key
117− fromKey (Key f) = Foreign.readNumber f
125+ toKey = Foreign.toForeign >>> Key
126+ fromKey (Key f) = Foreign.readNumber f
118127 unsafeFromKey (Key f) = Foreign.unsafeFromForeign f
119128
129+
120130instance idbKeyString :: IDBKey String where
121− toKey = Foreign.unsafeToForeign >>> Key
122− fromKey (Key f) = Foreign.readString f
131+ toKey = Foreign.toForeign >>> Key
132+ fromKey (Key f) = Foreign.readString f
123133 unsafeFromKey (Key f) = Foreign.unsafeFromForeign f
124134
135+
125136instance idbKeyDate :: IDBKey DateTime where
126− toKey (DateTime d t) = Key $ Fn.runFn7 _dateTimeToForeign
137+ toKey (DateTime d t) = Key $ Fn.runFn7 _dateTimeToForeign
127138 (fromEnum $ Date.year d)
128139 (fromEnum $ Date.month d)
129140 (fromEnum $ Date.day d)
@@ -131,51 +142,58 @@ instance idbKeyDate :: IDBKey DateTime where
131142 (fromEnum $ Time.minute t)
132143 (fromEnum $ Time.second t)
133144 (fromEnum $ Time.millisecond t)
134− fromKey (Key f) = Fn.runFn4 _readDateTime dateTime dateTimeF dateTimeE f
145+ fromKey (Key f) = Fn.runFn4 _readDateTime dateTime dateTimeF dateTimeE f
135146 unsafeFromKey (Key f) = Fn.runFn2 _unsafeReadDateTime dateTime f
136147
148+
137149instance idbKeyArray :: IDBKey a => IDBKey (Array a) where
138− toKey = Foreign.unsafeToForeign >>> Key
139− fromKey (Key f) = Foreign.readArray f >>= traverse (Key >>> fromKey)
150+ toKey = Foreign.toForeign >>> Key
151+ fromKey (Key f) = Foreign.readArray f >>= traverse (Key >>> fromKey)
140152 unsafeFromKey (Key f) = map unsafeFromKey (Foreign.unsafeFromForeign f)
141153
154+
142155-- FFI constructor to build a DateTime from years, months, days, hours, minutes, seconds and millis
143156dateTime
144− :: Int -- ^ years
145− -> Int -- ^ months
146− -> Int -- ^ days
147− -> Int -- ^ hours
148− -> Int -- ^ minutes
149− -> Int -- ^ seconds
150− -> Int -- ^ milliseconds
151− -> Nullable DateTime
157+ :: Int -- ^ years
158+ -> Int -- ^ months
159+ -> Int -- ^ days
160+ -> Int -- ^ hours
161+ -> Int -- ^ minutes
162+ -> Int -- ^ seconds
163+ -> Int -- ^ milliseconds
164+ -> Nullable DateTime
152165dateTime y m d h mi s ms =
153166 toNullable $ DateTime
154− <$> (Date.canonicalDate <$> toEnum y <*> toEnum m <*> toEnum d)
155− <*> (Time <$> toEnum h <*> toEnum mi <*> toEnum s <*> toEnum ms)
167+ <$> (Date.canonicalDate <$> toEnum y <*> toEnum m <*> toEnum d)
168+ <*> (Time <$> toEnum h <*> toEnum mi <*> toEnum s <*> toEnum ms)
169+
156170
157171-- FFI constructor to convert a JS `Date` into a successful `F DateTime`
158172dateTimeF
159− :: DateTime
160− -> F DateTime
173+ :: DateTime
174+ -> F DateTime
161175dateTimeF =
162176 Right >>> Identity >>> ExceptT
163177
178+
164179-- FFI constructor to convert a string into an errored `F DateTime`
165180dateTimeE
166− :: String
167− -> F DateTime
181+ :: String
182+ -> F DateTime
168183dateTimeE =
169184 Foreign.TypeMismatch "Date" >>> flip NonEmpty Nil >>> NonEmptyList >>> Left >>> Identity >>> ExceptT
170185
186+
171187--------------------
172188-- FFI
173189--
174190foreign import _dateTimeToForeign
175− :: Fn7 Int Int Int Int Int Int Int Foreign
191+ :: Fn7 Int Int Int Int Int Int Int Foreign
192+
176193
177194foreign import _readDateTime
178− :: Fn4 (Int -> Int -> Int -> Int -> Int -> Int -> Int -> Nullable DateTime) (DateTime -> F DateTime) (String -> F DateTime) Foreign (F DateTime)
195+ :: Fn4 (Int -> Int -> Int -> Int -> Int -> Int -> Int -> Nullable DateTime) (DateTime -> F DateTime) (String -> F DateTime) Foreign (F DateTime)
196+
179197
180198foreign import _unsafeReadDateTime
181− :: Fn2 (Int -> Int -> Int -> Int -> Int -> Int -> Int -> Nullable DateTime) Foreign DateTime
199+ :: Fn2 (Int -> Int -> Int -> Int -> Int -> Int -> Int -> Nullable DateTime) Foreign DateTime
src/Database/IndexedDB/IDBKeyRange.js+9−9
@@ -1,4 +1,4 @@
1−export function _bound(lower, upper, lowerOpen, upperOpen) {
1+exports._bound = function _bound(lower, upper, lowerOpen, upperOpen) {
22 try {
33 return IDBKeyRange.bound(lower, upper, lowerOpen, upperOpen);
44 } catch (e) {
@@ -6,34 +6,34 @@ export function _bound(lower, upper, lowerOpen, upperOpen) {
66 }
77};
88
9−export function _includes(range, key) {
9+exports._includes = function _includes(range, key) {
1010 return range.includes(key);
1111};
1212
13−export function _lower(range) {
13+exports._lower = function _lower(range) {
1414 return range.lower;
1515};
1616
17−export function _lowerBound(lower, open) {
17+exports._lowerBound = function _lowerBound(lower, open) {
1818 return IDBKeyRange.lowerBound(lower, open);
1919};
2020
21−export function _lowerOpen(range) {
21+exports._lowerOpen = function _lowerOpen(range) {
2222 return range.lowerOpen;
2323};
2424
25−export function _only(key) {
25+exports._only = function _only(key) {
2626 return IDBKeyRange.only(key);
2727};
2828
29−export function _upper(range) {
29+exports._upper = function _upper(range) {
3030 return range.upper;
3131};
3232
33−export function _upperBound(upper, open) {
33+exports._upperBound = function _upperBound(upper, open) {
3434 return IDBKeyRange.upperBound(upper, open);
3535};
3636
37−export function _upperOpen(range) {
37+exports._upperOpen = function _upperOpen(range) {
3838 return range.upperOpen;
3939};
src/Database/IndexedDB/IDBKeyRange.purs+79−81
@@ -1,76 +1,70 @@
11-- | A key has an associated type which is one of: number, date, string, binary, or array.
22module Database.IndexedDB.IDBKeyRange
3− -- * Types
4− ( Open
5−
6− -- * Constructors
3+ ( class IDBKeyRange, includes
74 , only
85 , lowerBound
96 , upperBound
107 , bound
11−
12− -- * Interface
13− , includes
14−
15− -- * Attributes
168 , lower
179 , upper
1810 , lowerOpen
1911 , upperOpen
2012 ) where
2113
22−import Prelude (($), (>>>), map)
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)
2321
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)
22+import Database.IndexedDB.Core (KeyRange)
23+import Database.IndexedDB.IDBKey.Internal (class IDBKey, Key(..), toKey, extractForeign)
2924
30−import Database.IndexedDB.Core (class IDBKeyRange, KeyRange)
31−import Database.IndexedDB.IDBKey.Internal (class IDBKey, Key, toKey, unsafeFromKey)
3225
3326--------------------
34−-- TYPES
27+-- INTERFACES
3528--
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 :: forall k. (IDBKey k) => range -> k -> Boolean
3633
3734-- | Type alias for open
3835type Open = Boolean
3936
40−--------------------
41−-- CONSTRUCTORS
42−--
4337
4438-- | Returns a new IDBKeyRange spanning only key.
4539only
46− :: forall a
47− . (IDBKey a)
48− => a
49− -> KeyRange
40+ :: forall a. (IDBKey a)
41+ => a
42+ -> KeyRange
5043only key =
51− _only (unsafeFromKey $ toKey key)
44+ _only (extractForeign $ toKey key)
45+
5246
5347-- | Returns a new IDBKeyRange starting at key with no upper bound.
5448-- | If `Open` is `true`, key is not included in the range.
5549lowerBound
56− :: forall a
57− . (IDBKey a)
58− => a
59− -> Open
60− -> KeyRange
50+ :: forall a. (IDBKey a)
51+ => a
52+ -> Open
53+ -> KeyRange
6154lowerBound key open =
62− Fn.runFn2 _lowerBound (unsafeFromKey $ toKey key) open
55+ Fn.runFn2 _lowerBound (extractForeign $ toKey key) open
56+
6357
6458-- | Returns a new IDBKeyRange with no lower bound and ending at key.
6559-- | If `Open` is `true`, key is not included in the range.
6660upperBound
67− :: forall a
68− . (IDBKey a)
69− => a
70− -> Open
71− -> KeyRange
61+ :: forall a. (IDBKey a)
62+ => a
63+ -> Open
64+ -> KeyRange
7265upperBound key open =
73− Fn.runFn2 _upperBound (unsafeFromKey $ toKey key) open
66+ Fn.runFn2 _upperBound (extractForeign $ toKey key) open
67+
7468
7569-- | Returns a new IDBKeyRange spanning from `lower` to `upper`.
7670-- | If `lowerOpen` is `true`, `lower` is not included in the range.
@@ -78,93 +72,97 @@ upperBound key open =
7872-- |
7973-- | It throws a `DataError` if the bound is invalid.
8074bound
81− :: forall key
82− . (IDBKey key)
83− => { lower :: key, upper :: key, lowerOpen :: Boolean, upperOpen :: Boolean }
84− -> Maybe KeyRange
75+ :: forall a. (IDBKey a)
76+ => { lower :: a, upper :: a, lowerOpen :: Boolean, upperOpen :: Boolean }
77+ -> Maybe KeyRange
8578bound { lower: key1, upper: key2, lowerOpen: open1, upperOpen: open2 } =
8679 toMaybe
87− $ Fn.runFn4 _bound (unsafeFromKey $ toKey key1) (unsafeFromKey $ toKey key2) open1 open2
80+ $ Fn.runFn4 _bound (extractForeign $ toKey key1) (extractForeign $ toKey key2) open1 open2
8881
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
10382
10483--------------------
10584-- ATTRIBUTES
10685--
10786-- | Returns lower bound if any.
10887lower
109− :: KeyRange
110− -> Maybe Key
88+ :: KeyRange
89+ -> Maybe Key
11190lower =
112− _lower >>> toMaybe >>> map toKey
91+ _lower >>> toMaybe >>> map Key
92+
11393
11494-- | Returns upper bound if any.
11595upper
116− :: KeyRange
117− -> Maybe Key
96+ :: KeyRange
97+ -> Maybe Key
11898upper =
119− _upper >>> toMaybe >>> map toKey
99+ _upper >>> toMaybe >>> map Key
100+
120101
121102-- | Returns true if the lower open flag is set, false otherwise.
122103lowerOpen
123− :: KeyRange
124− -> Boolean
104+ :: KeyRange
105+ -> Boolean
125106lowerOpen =
126107 _lowerOpen
127108
109+
128110-- | Returns true if the upper open flag is set, false otherwise.
129111upperOpen
130− :: KeyRange
131− -> Boolean
112+ :: KeyRange
113+ -> Boolean
132114upperOpen =
133115 _upperOpen
134116
117+
135118--------------------
136−-- FFI
119+-- INSTANCES
137120--
121+instance idbKeyRangeKeyRange :: IDBKeyRange KeyRange where
122+ includes range =
123+ toKey >>> extractForeign >>> Fn.runFn2 _includes range
138124
139−foreign import _only
140− :: Foreign
141− -> KeyRange
142−
143−foreign import _lowerBound
144− :: Fn2 Foreign Boolean KeyRange
145−
146−foreign import _upperBound
147− :: Fn2 Foreign Boolean KeyRange
148125
126+--------------------
127+-- FFI
128+--
149129foreign import _bound
150130 :: Fn4 Foreign Foreign Boolean Boolean (Nullable KeyRange)
151131
132+
152133foreign import _includes
153134 :: forall range
154− . Fn2 range Foreign Boolean
135+ . Fn2 range Foreign Boolean
136+
155137
156138foreign import _lower
157139 :: KeyRange
158140 -> Nullable Foreign
159141
160−foreign import _upper
161− :: KeyRange
162− -> Nullable Foreign
142+
143+foreign import _lowerBound
144+ :: Fn2 Foreign Boolean KeyRange
145+
163146
164147foreign import _lowerOpen
165148 :: KeyRange
166149 -> Boolean
167150
151+
152+foreign import _only
153+ :: Foreign
154+ -> KeyRange
155+
156+
157+foreign import _upper
158+ :: KeyRange
159+ -> Nullable Foreign
160+
161+
162+foreign import _upperBound
163+ :: Fn2 Foreign Boolean KeyRange
164+
165+
168166foreign import _upperOpen
169167 :: KeyRange
170168 -> Boolean
src/Database/IndexedDB/IDBObjectStore.js+28−55
@@ -1,6 +1,6 @@
11const errorHandler = function errorHandler(cb) {
22 return function _handler(e) {
3− cb(e.target.error);
3+ cb(new Error(e.target.error.name));
44 };
55};
66
@@ -15,44 +15,36 @@ const toArray = function toArray(xs) {
1515};
1616
1717
18−export function _add(store, value, key) {
19− return function aff(error, success) {
18+exports._add = function _add(store, value, key) {
19+ return function aff(success, error) {
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(e);
25+ error(new Error(e.name));
2626 }
27−
28− return function canceler(_, cancelerError) {
29− cancelerError(new Error("Can't cancel IDB Effects"));
30− };
3127 };
3228};
3329
34−export function _autoIncrement(store) {
30+exports._autoIncrement = function _autoIncrement(store) {
3531 return store.autoIncrement;
3632};
3733
38−export function _clear(store) {
39− return function aff(error, success) {
34+exports._clear = function _clear(store) {
35+ return function aff(success, error) {
4036 try {
4137 const request = store.clear();
4238 request.onsuccess = successHandler(success);
4339 request.onerror = errorHandler(error);
4440 } catch (e) {
45− error(e);
41+ error(new Error(e.name));
4642 }
47−
48− return function canceler(_, cancelerError) {
49− cancelerError(new Error("Can't cancel IDB Effects"));
50− };
5143 };
5244};
5345
54−export function _createIndex(store, name, path, params) {
55− return function aff(error, success) {
46+exports._createIndex = function _createIndex(store, name, path, params) {
47+ return function aff(success, error) {
5648 var keyPath;
5749
5850 try {
@@ -72,66 +64,50 @@ export function _createIndex(store, name, path, params) {
7264 const index = store.createIndex(name, keyPath, params);
7365 success(index);
7466 } catch (e) {
75− error(e);
67+ error(new Error(e.name));
7668 }
77−
78− return function canceler(_, cancelerError) {
79− cancelerError(new Error("Can't cancel IDB Effects"));
80− };
8169 };
8270};
8371
84−export function _deleteIndex(store, name) {
85− return function aff(error, success) {
72+exports._deleteIndex = function _deleteIndex(store, name) {
73+ return function aff(success, error) {
8674 try {
8775 store.deleteIndex(name);
8876 success();
8977 } catch (e) {
90− error(e);
78+ error(new Error(e.name));
9179 }
92−
93− return function canceler(_, cancelerError) {
94− cancelerError(new Error("Can't cancel IDB Effects"));
95− };
9680 };
9781};
9882
99−export function _delete(store, query) {
100− return function aff(error, success) {
83+exports._delete = function _delete(store, query) {
84+ return function aff(success, error) {
10185 try {
10286 const request = store.delete(query);
10387 request.onsuccess = successHandler(success);
10488 request.onerror = errorHandler(error);
10589 } catch (e) {
106− error(e);
90+ error(new Error(e.name));
10791 }
108−
109− return function canceler(_, cancelerError) {
110− cancelerError(new Error("Can't cancel IDB Effects"));
111− };
11292 };
11393};
11494
115−export function _index(store, name) {
116− return function aff(error, success) {
95+exports._index = function _index(store, name) {
96+ return function aff(success, error) {
11797 try {
11898 const index = store.index(name);
11999 success(index);
120100 } catch (e) {
121− error(e);
101+ error(new Error(e.name));
122102 }
123−
124− return function canceler(_, cancelerError) {
125− cancelerError(new Error("Can't cancel IDB Effects"));
126− };
127103 };
128104};
129105
130−export function _indexNames(store) {
106+exports._indexNames = function _indexNames(store) {
131107 return toArray(store.indexNames);
132108};
133109
134−export function _keyPath(store) {
110+exports._keyPath = function _keyPath(store) {
135111 const path = store.keyPath;
136112
137113 if (Array.isArray(path)) {
@@ -145,26 +121,23 @@ export function _keyPath(store) {
145121 return [];
146122};
147123
148−export function _name(store) {
124+exports._name = function _name(store) {
149125 return store.name;
150126};
151127
152−export function _put(store, value, key) {
153− return function aff(error, success) {
128+exports._put = function _put(store, value, key) {
129+ return function aff(success, error) {
154130 try {
155131 const request = store.put(value, key || undefined);
156132 request.onsuccess = successHandler(success);
157133 request.onerror = errorHandler(error);
158134 } catch (e) {
159− error(e);
135+ error(new Error(e.name));
160136 }
161−
162− return function canceler(_, cancelerError) {
163− cancelerError(new Error("Can't cancel IDB Effects"));
164− };
165137 };
166138};
167139
168−export function _transaction(store) {
140+exports._transaction = function _transaction(store) {
169141 return store.transaction;
170142};
143+
src/Database/IndexedDB/IDBObjectStore.purs+156−160
@@ -1,170 +1,124 @@
11-- | An object store is the primary storage mechanism for storing data in a database.
22module Database.IndexedDB.IDBObjectStore
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
3+ ( class IDBObjectStore, add, clear, createIndex, delete, deleteIndex, index, put
4+ , module Database.IndexedDB.IDBIndex.Internal
5+ , IDBObjectStoreParameters
6+ , IndexName
187 , autoIncrement
198 , indexNames
209 , keyPath
2110 , name
2211 , transaction
23−
24− -- * Re-Exports
25− , module Database.IndexedDB.IDBIndex
12+ , defaultParameters
2613 ) where
2714
28−import Database.IndexedDB.Core
15+import Prelude (Unit, ($), (<$>), (>>>))
16+
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)
23+
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)
2927
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, ($), (<$>), (>>>), (<<<))
4028
4129--------------------
42−-- TYPES
30+-- INTERFACES
4331--
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+
44104
45105-- | Type alias for IndexName
46106type IndexName = String
47107
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
108+
109+-- | Options provided when creating an object store.
110+type IDBObjectStoreParameters =
111+ { keyPath :: KeyPath
112+ , autoIncrement :: Boolean
64113 }
65114
66−defaultParameters :: IndexParameters
115+
116+defaultParameters :: IDBObjectStoreParameters
67117defaultParameters =
68− { unique: false
69− , multiEntry: false
118+ { keyPath : []
119+ , autoIncrement : false
70120 }
71121
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)
168122
169123--------------------
170124-- ATTRIBUTES
@@ -176,6 +130,7 @@ autoIncrement
176130autoIncrement =
177131 _autoIncrement
178132
133+
179134--| Returns a list of the names of indexes in the store.
180135indexNames
181136 :: ObjectStore
@@ -183,6 +138,7 @@ indexNames
183138indexNames =
184139 _indexNames
185140
141+
186142-- | Returns the key path of the store, or empty array if none
187143keyPath
188144 :: ObjectStore
@@ -190,6 +146,7 @@ keyPath
190146keyPath =
191147 _keyPath
192148
149+
193150-- | Returns the name of the store.
194151name
195152 :: ObjectStore
@@ -197,6 +154,7 @@ name
197154name =
198155 _name
199156
157+
200158-- | Returns the associated transaction.
201159transaction
202160 :: ObjectStore
@@ -204,53 +162,91 @@ transaction
204162transaction =
205163 _transaction
206164
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+
207192--------------------
208193-- FFI
209194--
210195foreign import _add
211− :: forall val store
212− . Fn3 store val (Nullable Foreign) (EffectFnAff Foreign)
196+ :: forall value e
197+ . Fn3 ObjectStore value (Nullable Foreign) (Aff (idb :: IDB | e) Foreign)
198+
213199
214200foreign import _autoIncrement
215201 :: ObjectStore
216202 -> Boolean
217203
204+
218205foreign import _clear
219− :: forall store
220− . store
221− -> EffectFnAff Unit
206+ :: forall e
207+ . ObjectStore
208+ -> Aff (idb :: IDB | e) Unit
209+
222210
223211foreign import _createIndex
224− :: forall store
225− . Fn4 store String (Array String) { unique :: Boolean, multiEntry :: Boolean } (EffectFnAff Index)
212+ :: forall e
213+ . Fn4 ObjectStore String (Array String) { unique :: Boolean, multiEntry :: Boolean } (Aff (idb :: IDB | e) Index)
214+
226215
227216foreign import _delete
228− :: forall store
229− . Fn2 store KeyRange (EffectFnAff Unit)
217+ :: forall e
218+ . Fn2 ObjectStore KeyRange (Aff (idb :: IDB | e) Unit)
219+
230220
231221foreign import _deleteIndex
232− :: forall store
233− . Fn2 store String (EffectFnAff Unit)
222+ :: forall e
223+ . Fn2 ObjectStore String (Aff (idb :: IDB | e) Unit)
224+
234225
235226foreign import _index
236− :: forall store
237− . Fn2 store String (EffectFnAff Index)
227+ :: forall e
228+ . Fn2 ObjectStore String (Aff (idb :: IDB | e) Index)
229+
238230
239231foreign import _indexNames
240232 :: ObjectStore
241233 -> Array String
242234
235+
243236foreign import _keyPath
244237 :: ObjectStore
245238 -> Array String
246239
240+
247241foreign import _name
248242 :: ObjectStore
249243 -> String
250244
245+
251246foreign import _put
252− :: forall val store
253− . Fn3 store val (Nullable Foreign) (EffectFnAff Foreign)
247+ :: forall value e
248+ . Fn3 ObjectStore value (Nullable Foreign) (Aff (idb :: IDB | e) Foreign)
249+
254250
255251foreign import _transaction
256252 :: ObjectStore
src/Database/IndexedDB/IDBTransaction.js+18−38
@@ -1,29 +1,25 @@
1−export function _abort(tx) {
2− return function aff(error, success) {
1+exports._abort = function _abort(tx) {
2+ return function aff(success, error) {
33 try {
44 tx.abort();
55 success();
66 } catch (e) {
7− error(e);
7+ error(new Error(e.name));
88 }
9−
10− return function canceler(_, cancelerError) {
11− cancelerError(new Error("Can't cancel IDB Effects"));
12− };
139 };
1410};
1511
16−export function _db(tx) {
12+exports._db = function _db(tx) {
1713 return tx.db;
1814};
1915
20−export function _error(tx) {
16+exports._error = function _error(tx) {
2117 return tx.error == null
2218 ? null
23− : tx.error;
19+ : new Error(tx.error.name);
2420};
2521
26−export function _mode(ReadOnly, ReadWrite, VersionChange, tx) {
22+exports._mode = function _mode(ReadOnly, ReadWrite, VersionChange, tx) {
2723 if (tx.mode === 'readwrite') {
2824 return ReadWrite;
2925 } else if (tx.mode === 'versionchange') {
@@ -33,60 +29,44 @@ export function _mode(ReadOnly, ReadWrite, VersionChange, tx) {
3329 return ReadOnly;
3430};
3531
36−export function _objectStore(tx, name) {
37− return function aff(error, success) {
32+exports._objectStore = function _objectStore(tx, name) {
33+ return function aff(success, error) {
3834 try {
3935 const store = tx.objectStore(name);
4036 success(store);
4137 } catch (e) {
42− error(e);
38+ error(new Error(e.name));
4339 }
44−
45− return function canceler(_, cancelerError) {
46− cancelerError(new Error("Can't cancel IDB Effects"));
47− };
4840 };
4941};
5042
51−export function _objectStoreNames(tx) {
43+exports._objectStoreNames = function _objectStoreNames(tx) {
5244 return tx.objectStoreNames;
5345};
5446
55−export function _onAbort(tx, f) {
56− return function aff(error, success) {
47+exports._onAbort = function _onAbort(tx, f) {
48+ return function aff(success) {
5749 tx.onabort = function onabort() {
5850 f();
5951 };
6052 success();
61−
62− return function canceler(_, cancelerError) {
63− cancelerError(new Error("Can't cancel IDB Effects"));
64− };
6553 };
6654};
6755
68−export function _onComplete(tx, f) {
69− return function aff(error, success) {
56+exports._onComplete = function _onComplete(tx, f) {
57+ return function aff(success) {
7058 tx.oncomplete = function oncomplete() {
7159 f();
7260 };
7361 success();
74−
75− return function canceler(_, cancelerError) {
76− cancelerError(new Error("Can't cancel IDB Effects"));
77− };
7862 };
7963};
8064
81−export function _onError(tx, f) {
82− return function aff(error, success) {
65+exports._onError = function _onError(tx, f) {
66+ return function aff(success) {
8367 tx.onerror = function onerror(e) {
84− f(e.target.error)();
68+ f(new Error(e.target.error.name))();
8569 };
8670 success();
87−
88− return function canceler(_, cancelerError) {
89− cancelerError(new Error("Can't cancel IDB Effects"));
90− };
9171 };
9272};
src/Database/IndexedDB/IDBTransaction.purs+83−66
@@ -1,66 +1,58 @@
1−-- | An object store is the primary storage mechanism for storing data in a database.
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.
23module Database.IndexedDB.IDBTransaction
3− -- * Interface
4− ( abort
5− , objectStore
6−
7− -- * Attributes
4+ (class IDBTransaction, abort, objectStore
85 , error
96 , mode
10−
11− -- * Event Handlers
127 , onAbort
138 , onComplete
149 , onError
1510 ) where
1611
17−import Database.IndexedDB.Core
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(..))
1823
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, ($), (<<<), (>>>))
2824
2925--------------------
3026-- INTERFACES
3127--
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
3243
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
5244
5345--------------------
5446-- ATTRIBUTES
5547--
56−
57−--- | Returns the transaction’s connection.
48+-- | Returns the transaction’s connection.
5849db
5950 :: Transaction
6051 -> Database
6152db =
6253 _db
6354
55+
6456-- | If the transaction was aborted, returns the error (a DOMException) providing the reason.
6557error
6658 :: Transaction
@@ -76,6 +68,7 @@ mode
7668mode =
7769 Fn.runFn4 _mode ReadOnly ReadWrite VersionChange
7870
71+
7972-- | Returns a list of the names of object stores in the transaction’s scope.
8073-- | For an upgrade transaction this is all object stores in the database.
8174objectStoreNames
@@ -87,67 +80,91 @@ objectStoreNames =
8780--------------------
8881-- EVENT HANDLERS
8982--
90−
9183-- | Event handler for the `abort` event.
9284onAbort
93− :: Transaction
94− -> Effect Unit
95− -> Aff Unit
96−onAbort db' f =
97− fromEffectFnAff $ Fn.runFn2 _onAbort db' f
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+
9892
9993-- | Event handler for the `complete` event.
10094onComplete
101− :: Transaction
102− -> Effect Unit
103− -> Aff Unit
104−onComplete db' f =
105− fromEffectFnAff $ Fn.runFn2 _onComplete db' f
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+
106102
107103-- | Event handler for the `error` event.
108104onError
109− :: Transaction
110− -> (Error -> Effect Unit)
111− -> Aff Unit
105+ :: forall e e'
106+ . Transaction
107+ -> (Error -> Eff ( | e') Unit)
108+ -> Aff (idb :: IDB | e) Unit
112109onError db' f =
113− fromEffectFnAff $ Fn.runFn2 _onError db' f
110+ Fn.runFn2 _onError db' f
111+
114112
115113--------------------
116−-- FFI
114+-- INSTANCES
117115--
116+instance idbTransactionTransaction :: IDBTransaction Transaction where
117+ objectStore tx name =
118+ Fn.runFn2 _objectStore tx name
118119
120+ abort =
121+ _abort
122+
123+
124+--------------------
125+-- FFI
126+--
119127foreign import _abort
120− :: forall tx
121− . tx
122− -> EffectFnAff Unit
128+ :: forall tx e
129+ . tx
130+ -> Aff (idb :: IDB | e) Unit
131+
123132
124133foreign import _db
125134 :: Transaction
126135 -> Database
127136
137+
128138foreign import _error
129139 :: Transaction
130140 -> (Nullable Error)
131141
142+
132143foreign import _mode
133144 :: Fn4 TransactionMode TransactionMode TransactionMode Transaction TransactionMode
134145
146+
135147foreign import _objectStoreNames
136148 :: Transaction
137149 -> Array String
138150
151+
139152foreign import _objectStore
140− :: forall tx
141− . Fn2 tx String (EffectFnAff ObjectStore)
153+ :: forall tx e
154+ . Fn2 tx String (Aff (idb :: IDB | e) ObjectStore)
155+
142156
143157foreign import _onAbort
144− :: forall tx
145− . Fn2 tx (Effect Unit) (EffectFnAff Unit)
158+ :: forall tx e e'
159+ . Fn2 tx (Eff ( | e') Unit) (Aff (idb :: IDB | e) Unit)
160+
146161
147162foreign import _onComplete
148− :: forall tx
149− . Fn2 tx (Effect Unit) (EffectFnAff Unit)
163+ :: forall tx e e'
164+ . Fn2 tx (Eff ( | e') Unit) (Aff (idb :: IDB | e) Unit)
165+
150166
151167foreign import _onError
152− :: forall tx
153− . Fn2 tx (Error -> Effect Unit) (EffectFnAff Unit)
168+ :: forall tx e e'
169+ . Fn2 tx (Error -> Eff ( | e') Unit) (Aff (idb :: IDB | e) Unit)
170+
test/Main.purs+904−974
@@ -2,1060 +2,995 @@ module Test.Main where
22
33import Prelude
44
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)
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
3636
37−infixr 7 Tuple as :+:
3837
39−launchAff' :: forall a. Aff a -> Effect Unit
40−launchAff' =
41− launchAff_ <<< void
38+infixr 7 Tuple as :+:
4239
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
40+launchAff' :: forall a e. Aff e a -> Eff (exception :: EXCEPTION | e) Unit
41+launchAff' aff =
42+ pure unit <* (launchAff aff)
4743
48−main :: Effect Unit
49−main = do
50− launchAff_ $ runSpec [ consoleReporter ] $ do
51− describe "IDBFactory" do
52− let
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
5348 tearDown name version db = do
5449 IDBDatabase.close db
5550 version' <- IDBFactory.deleteDatabase name
56− liftEffect $ log $ show version'
5751 version' `shouldEqual` version
5852
59− it "open default" do
60− let
61− name = "db-default"
53+ it "open default" do
54+ let name = "db-default"
6255 version = 1
63− db <- IDBFactory.open name Nothing
64− { onUpgradeNeeded: Nothing
65− , onBlocked: Nothing
66− }
67− IDBDatabase.name db `shouldEqual` name
68− tearDown name version db
56+ db <- IDBFactory.open name Nothing
57+ { onUpgradeNeeded : Nothing
58+ , onBlocked : Nothing
59+ }
60+ IDBDatabase.name db `shouldEqual` name
61+ tearDown name version db
6962
70− it "open specific version" do
71− let
72− name = "db-specific"
73− version = 14
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
8063
81− it "open specific version -> close -> open latest" do
82− let
83− name = "db-latest"
64+ it "open specific version" do
65+ let name = "db-specific"
8466 version = 14
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
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
9673
97− it "open + onUpgradeNeed" do
98− let
99− name = "db-upgrade-needed"
74+
75+ it "open specific version -> close -> open latest" do
76+ let name = "db-latest"
77+ 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"
10093 version = 1
101− callback (Tuple varName varVersion) db _ { oldVersion } = do
102− _ <- launchAff $ modifyAVar (const $ IDBDatabase.name db) varName
103− _ <- launchAff $ modifyAVar (const $ oldVersion) varVersion
94+ callback var db _ = do
95+ _ <- launchAff $ modifyVar (const $ IDBDatabase.name db) var
10496 pure unit
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"
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"
120109 version = 14
121110 callback var = do
122− _ <- launchAff $ modifyAVar (const $ "db-blocked") var
111+ _ <- launchAff $ modifyVar (const $ "db-blocked") var
123112 pure unit
124113
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
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
133122
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
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
146134 range = IDBKeyRange.only key
147− IDBKeyRange.includes range (toKey key) `shouldEqual` true
135+ IDBKeyRange.includes range (toKey key) `shouldEqual` true
148136
149− it "only(string)" do
150− let
151− key = "patate"
137+ it "only(string)" do
138+ let key = "patate"
152139 range = IDBKeyRange.only key
153− IDBKeyRange.includes range (toKey key) `shouldEqual` true
140+ IDBKeyRange.includes range (toKey key) `shouldEqual` true
154141
155− it "only(float)" do
156− let
157− key = 14.42
142+ it "only(float)" do
143+ let key = 14.42
158144 range = IDBKeyRange.only key
159− IDBKeyRange.includes range (toKey key) `shouldEqual` true
145+ IDBKeyRange.includes range (toKey key) `shouldEqual` true
160146
161− it "only(date)" do
162− let
163− mkey = DateTime
147+ it "only(date)" do
148+ let mkey = DateTime
164149 <$> (Date.canonicalDate <$> toEnum 2017 <*> toEnum 6 <*> toEnum 23)
165150 <*> (Time <$> toEnum 17 <*> toEnum 59 <*> toEnum 34 <*> toEnum 42)
166151
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
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
173158
174− it "only([int])" do
175− let
176− key = [ 14, 42 ]
159+ it "only([int])" do
160+ let key = [14, 42]
177161 range = IDBKeyRange.only key
178− IDBKeyRange.includes range (toKey key) `shouldEqual` true
162+ IDBKeyRange.includes range (toKey key) `shouldEqual` true
179163
180− it "only([string])" do
181− let
182− key = [ "patate", "autruche" ]
164+ it "only([string])" do
165+ let key = ["patate", "autruche"]
183166 range = IDBKeyRange.only key
184− IDBKeyRange.includes range (toKey key) `shouldEqual` true
167+ IDBKeyRange.includes range (toKey key) `shouldEqual` true
185168
186− it "lowerBound(14, open)" do
187− let
188− key = 14
189− open = true
169+ it "lowerBound(14, open)" do
170+ let key = 14
171+ open = true
190172 range = IDBKeyRange.lowerBound key open
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
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
199180 range = IDBKeyRange.lowerBound key open
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
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
208188 range = IDBKeyRange.upperBound key open
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
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
217196 range = IDBKeyRange.upperBound key open
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
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
226204 lowerOpen = true
227205 upperOpen = true
228− mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
229− isNothing mrange `shouldEqual` true
206+ mrange = IDBKeyRange.bound { lower, upper, lowerOpen, upperOpen }
207+ isNothing mrange `shouldEqual` true
230208
231− it "bound(14, 42, open, open)" do
232− let
233− lower = 14
234− upper = 42
209+ it "bound(14, 42, open, open)" do
210+ let lower = 14
211+ upper = 42
235212 lowerOpen = true
236213 upperOpen = true
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
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
253229 lowerOpen = true
254230 upperOpen = false
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
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
271246 lowerOpen = false
272247 upperOpen = true
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
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
289263 lowerOpen = false
290264 upperOpen = false
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
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
312286 tearDown db = do
313287 IDBDatabase.close db
314288 _ <- IDBFactory.deleteDatabase (IDBDatabase.name db)
315289 pure unit
316290
317291 setup storeParams = do
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
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
328301 }
329302
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
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
359331 _ <- IDBDatabase.deleteObjectStore db "store"
360− AVar.put true var
361−
362− var <- AVar.empty
363− { db } <- setup IDBDatabase.defaultParameters
364− IDBDatabase.close db
365− db' <- IDBFactory.open "db" (Just 999)
366− { onUpgradeNeeded: Just (onUpgradeNeeded var)
367− , onBlocked: Nothing
368− }
369− deleted <- AVar.take var
370− deleted `shouldEqual` true
371− tearDown db'
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
372343
373− describe "IDBObjectStore" do
374− let
344+
345+ describe "IDBObjectStore" do
346+ let
375347 tearDown db = do
376348 IDBDatabase.close db
377349 _ <- IDBFactory.deleteDatabase (IDBDatabase.name db)
378350 pure unit
379351
380352 setup { storeParams, onUpgradeNeeded } = do
381− let
382− onUpgradeNeeded' var db _ _ = launchAff' do
383− store <- IDBDatabase.createObjectStore db "store" storeParams
384− liftEffect $ maybe (pure unit) identity (onUpgradeNeeded <*> pure db <*> pure store)
385− AVar.put { db, store } var
386−
387− var <- AVar.empty
388− _ <- IDBFactory.open "db" Nothing
389− { onUpgradeNeeded: Just (onUpgradeNeeded' var)
390− , onBlocked: Nothing
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
391362 }
392363
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)
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
434429 }
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'
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'
492462 , onComplete: pure unit
493463 }
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
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
505475
506− it "openKeyCursor()" do
507− let
508− cb =
509− { onSuccess: const $ pure unit
510− , onError: show >>> fail >>> launchAff'
476+ it "openKeyCursor()" do
477+ let cb =
478+ { onSuccess : const $ pure unit
479+ , onError : show >>> fail >>> launchAff'
511480 , onComplete: pure unit
512481 }
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
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
524493
525− describe "IDBIndex" do
526− let
494+ describe "IDBIndex" do
495+ let
527496 tearDown db = do
528497 IDBDatabase.close db
529498 _ <- IDBFactory.deleteDatabase (IDBDatabase.name db)
530499 pure unit
531500
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 }
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 }
541508 setup { storeParams, indexParams, values, keyPath, onUpgradeNeeded } = do
542− let
543− onUpgradeNeeded' var db tx _ = launchAff' do
544− store <- IDBDatabase.createObjectStore db "store" storeParams
545− _ <- traverse (uncurry (IDBObjectStore.add store)) values
546− index <- IDBObjectStore.createIndex store "index" keyPath indexParams
547− liftEffect $ maybe (pure unit) identity (onUpgradeNeeded <*> pure db <*> pure tx <*> pure index)
548− AVar.put { db, index, store } var
549−
550− var <- AVar.empty
551− _ <- IDBFactory.open "db" Nothing
552− { onUpgradeNeeded: Just (onUpgradeNeeded' var)
553− , onBlocked: Nothing
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
554520 }
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'
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'
699654 , onComplete: pure unit
700655 }
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'
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'
727680 , onComplete: pure unit
728681 }
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'
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'
754705 , onComplete: pure unit
755706 }
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'
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'
779728 , onComplete: pure unit
780729 }
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'
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'
807754 , onComplete: pure unit
808755 }
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'
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'
834779 , onComplete: pure unit
835780 }
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
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
890834 tearDown db = do
891835 IDBDatabase.close db
892836 _ <- IDBFactory.deleteDatabase (IDBDatabase.name db)
893837 pure unit
894838
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 }
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 }
904846 setup { storeParams, indexParams, values, keyPath, onUpgradeNeeded } = do
905− let
906− onUpgradeNeeded' var db tx _ = launchAff' do
907− store <- IDBDatabase.createObjectStore db "store" storeParams
908− _ <- traverse (uncurry (IDBObjectStore.add store)) values
909− index <- IDBObjectStore.createIndex store "index" keyPath indexParams
910− liftEffect $ maybe (pure unit) identity (onUpgradeNeeded <*> pure db <*> pure tx <*> pure index)
911− AVar.put { db, index, store } var
912−
913− var <- AVar.empty
914− db <- IDBFactory.open "db" Nothing
915− { onUpgradeNeeded: Just (onUpgradeNeeded' var)
916− , onBlocked: Nothing
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
917858 }
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 =
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 =
935874 { onComplete: launchAff' do
936− AVar.put unit vdone
875+ putVar vdone unit
937876
938− , onError: \error -> launchAff' do
877+ , onError: \error -> launchAff' do
939878 fail $ "unexpected error: " <> show error
940879
941880 , onSuccess: \cursor -> launchAff' do
942− vals <- AVar.take vvals
881+ vals <- takeVar vvals
943882 pure (IDBCursor.value cursor) >>= shouldEqual (maybe "" _.v $ head vals)
944− IDBCursor.primaryKey cursor >>= shouldEqual (maybe (toKey 0) _.k $ head vals)
945− AVar.put (drop 1 vals) vvals
883+ IDBCursor.primaryKey cursor >>= shouldEqual (maybe (toKey 0) _.k $ head vals)
884+ putVar vvals (drop 1 vals)
946885 IDBCursor.continue cursor none
947886 }
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 =
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 =
976913 { onComplete: launchAff' do
977914 fail $ "shouldn't complete"
978915
979− , onError: \error -> launchAff' do
916+ , onError: \error -> launchAff' do
980917 fail $ "unexpected error: " <> show error
981918
982919 , onSuccess: \cursor -> launchAff' do
983920 res <- attempt $ IDBCursor.continue cursor (Just 1)
984921 case res of
985− Left err -> do
986− name err `shouldEqual` "DataError"
987− AVar.put unit vdone
922+ Left err -> do
923+ message err `shouldEqual` "DataError"
924+ putVar vdone unit
988925 Right _ -> do
989926 fail "expected continue to fail"
990927 }
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 =
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 =
1013949 { onComplete: launchAff' do
1014− AVar.put unit vdone
950+ putVar vdone unit
1015951
1016− , onError: \error -> launchAff' do
952+ , onError: \error -> launchAff' do
1017953 fail $ "unexpected error: " <> show error
1018954
1019955 , onSuccess: \cursor -> launchAff' do
1020− jump <- AVar.take vjump
1021− if jump then do
1022− IDBCursor.advance cursor 3
1023− else do
1024− let value = IDBCursor.value cursor
1025− value.pKey `shouldEqual` "pkey_3"
1026− value.iKey `shouldEqual` "ikey_3"
1027− IDBCursor.continue cursor none
1028− AVar.put false vjump
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
1029966 }
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 =
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 =
1053988 { onComplete: launchAff' do
1054989 mval <- map _.pKey <$> IDBIndex.get store (IDBKeyRange.only "pkey_0")
1055990 mval `shouldEqual` (Nothing :: Maybe String)
1056− AVar.put unit vdone
991+ putVar vdone unit
1057992
1058− , onError: \error -> launchAff' do
993+ , onError: \error -> launchAff' do
1059994 fail $ "unexpected error: " <> show error
1060995
1061996 , onSuccess: \cursor -> launchAff' do
@@ -1064,31 +999,29 @@ main = do
1064999 IDBCursor.delete cursor
10651000 IDBCursor.advance cursor 4
10661001 }
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 =
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 =
10861019 { onComplete: launchAff' do
10871020 mval <- map _.iKey <$> IDBIndex.get store (IDBKeyRange.only "pkey_0")
10881021 mval `shouldEqual` (Just "patate")
1089− AVar.put unit vdone
1022+ putVar vdone unit
10901023
1091− , onError: \error -> launchAff' do
1024+ , onError: \error -> launchAff' do
10921025 fail $ "unexpected error: " <> show error
10931026
10941027 , onSuccess: \cursor -> launchAff' do
@@ -1098,44 +1031,41 @@ main = do
10981031 key `shouldEqual` toKey "pkey_0"
10991032 IDBCursor.advance cursor 4
11001033 }
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 =
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 =
11201051 { onComplete: launchAff' do
11211052 fail $ "shouldn't complete"
11221053
1123− , onError: \error -> launchAff' do
1054+ , onError: \error -> launchAff' do
11241055 fail $ "unexpected error: " <> show error
11251056
11261057 , onSuccess: \cursor -> launchAff' do
11271058 res <- attempt $ IDBCursor.update cursor "patate"
11281059 case res of
11291060 Left err -> do
1130− name err `shouldEqual` "ReadOnlyError"
1131− AVar.put unit vdone
1061+ message err `shouldEqual` "ReadOnlyError"
1062+ putVar vdone unit
11321063 Right _ ->
11331064 fail $ "expected ReadOnlyError"
11341065 }
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−
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
yarn.lockdeleted−81
@@ -1,81 +0,0 @@
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"