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