├── samples ├── angular2 │ ├── src │ │ ├── assets │ │ │ └── .gitkeep │ │ ├── environments │ │ │ ├── environment.prod.ts │ │ │ └── environment.ts │ │ ├── favicon.ico │ │ ├── styles.css │ │ ├── app │ │ │ ├── core │ │ │ │ ├── dexie.service.ts │ │ │ │ └── core.module.ts │ │ │ ├── app-add-todo.component.ts │ │ │ ├── app.module.ts │ │ │ ├── todos.service.ts │ │ │ ├── app-todo-list.component.ts │ │ │ └── app.component.ts │ │ ├── index.html │ │ ├── main.ts │ │ ├── tsconfig.json │ │ ├── polyfills.ts │ │ └── test.ts │ ├── .editorconfig │ ├── e2e │ │ └── tsconfig.json │ ├── .gitignore │ ├── protractor.conf.js │ ├── karma.conf.js │ ├── README.md │ ├── angular-cli.json │ └── package.json ├── typescript │ ├── .gitignore │ ├── src │ │ ├── app.css │ │ ├── console.ts │ │ └── app.html │ ├── tsconfig.json │ ├── rollup.config.js │ ├── package.json │ └── README.md ├── react │ ├── src │ │ ├── Todo.css │ │ ├── index.css │ │ ├── db.js │ │ ├── index.js │ │ ├── App.css │ │ ├── Todo.js │ │ ├── TodoList.js │ │ └── AddTodo.js │ ├── public │ │ ├── favicon.ico │ │ └── index.html │ ├── .gitignore │ ├── package.json │ └── README.md ├── typescript-simple │ ├── .gitignore │ ├── app-init.js │ ├── README.md │ ├── tsconfig.json │ ├── index.html │ ├── app.ts │ └── package.json ├── react-redux │ ├── src │ │ ├── components │ │ │ ├── Todo.css │ │ │ ├── App.css │ │ │ ├── Todo.js │ │ │ ├── TodoList.js │ │ │ ├── AddTodo.js │ │ │ └── App.js │ │ ├── index.css │ │ ├── db.js │ │ ├── constants.js │ │ ├── store │ │ │ └── index.js │ │ ├── index.js │ │ ├── containers │ │ │ └── App.js │ │ ├── reducers │ │ │ └── index.js │ │ └── actions │ │ │ └── index.js │ ├── public │ │ ├── favicon.ico │ │ └── index.html │ ├── .gitignore │ ├── package.json │ └── README.md ├── browserify │ ├── app.css │ ├── app.html │ ├── README.md │ ├── scripts │ │ ├── console.js │ │ ├── contact.js │ │ ├── main.js │ │ └── db.js │ └── package.json ├── requirejs │ ├── app.css │ ├── README.md │ ├── app.html │ └── scripts │ │ ├── console.js │ │ ├── Contact.js │ │ ├── app.js │ │ └── db.js ├── requirejs-with-addons │ ├── app.css │ ├── README.md │ ├── scripts │ │ ├── ChangeLogger.js │ │ ├── console.js │ │ ├── db.js │ │ └── app.js │ └── app.html └── electron │ ├── package.json │ ├── index.html │ ├── README.md │ └── main.js ├── src ├── errors │ ├── index.ts │ └── errors.d.ts ├── classes │ ├── dexie │ │ ├── index.ts │ │ ├── vip.ts │ │ └── generate-middleware-stacks.ts │ ├── table │ │ ├── index.ts │ │ └── table-constructor.ts │ ├── collection │ │ └── index.ts │ ├── transaction │ │ └── index.ts │ ├── version │ │ └── version-constructor.ts │ └── where-clause │ │ └── where-clause-constructor.ts ├── public │ └── types │ │ ├── then-shortcut.d.ts │ │ ├── transaction-mode.d.ts │ │ ├── db-schema.d.ts │ │ ├── dexie-dom-dependencies.d.ts │ │ ├── dexie-event.d.ts │ │ ├── version.d.ts │ │ ├── index-spec.d.ts │ │ ├── table-schema.d.ts │ │ ├── middleware.d.ts │ │ ├── indexable-type.d.ts │ │ ├── transaction-events.d.ts │ │ ├── dexie-event-set.d.ts │ │ ├── transaction.d.ts │ │ ├── db-events.d.ts │ │ ├── database.d.ts │ │ ├── table-hooks.d.ts │ │ └── where-clause.d.ts ├── functions │ ├── is-promise-like.ts │ ├── combine.ts │ ├── compare-functions.ts │ ├── quirks.ts │ ├── stringify-key.d.ts │ └── make-class-constructor.ts ├── dbcore │ ├── keyrange.ts │ ├── get-key-extractor.ts │ ├── get-effective-keys.ts │ ├── key-resolver-middleware.ts │ ├── proxy-cursor.ts │ └── lockable-table-middleware.ts ├── tsconfig.json ├── helpers │ ├── table-schema.ts │ ├── index-spec.ts │ └── yield-support.ts ├── globals │ └── constants.ts └── index.ts ├── test ├── .gitignore ├── integrations │ ├── dexie-relationships │ │ ├── index.js │ │ ├── travis.sh │ │ ├── rollup.config.js │ │ └── karma.conf.js │ └── package.json ├── travis.sh ├── karma-env.js ├── .eslintrc.json ├── typings-test │ ├── tsconfig.json │ ├── test-misc.ts │ └── test-extend-dexie.ts ├── tsconfig.json ├── karma.conf.js ├── tests-all.js ├── run-unit-tests.html ├── worker.js ├── is-idb-and-promise-compatible.js ├── karma.browsers.matrix.js └── karma.browserstack.js ├── addons ├── dexie-export-import │ ├── test │ │ ├── index.ts │ │ ├── .gitignore │ │ ├── travis.sh │ │ ├── qunit.d.ts │ │ ├── tsconfig.json │ │ ├── index.html │ │ ├── karma.conf.js │ │ └── tools.ts │ ├── src │ │ ├── index.ts │ │ ├── tson-arraybuffer.ts │ │ ├── tsconfig.json │ │ ├── json-structure.ts │ │ ├── tson-typed-array.ts │ │ └── dexie-export-import.ts │ ├── .npmignore │ ├── .gitignore │ ├── tools │ │ └── build-configs │ │ │ ├── banner.txt │ │ │ ├── rollup.tests.config.js │ │ │ └── rollup.config.js │ └── package.json ├── Dexie.Observable │ ├── test │ │ ├── unit │ │ │ ├── .gitignore │ │ │ ├── travis.sh │ │ │ ├── unit-tests-all.js │ │ │ ├── .eslintrc.json │ │ │ ├── run-unit-tests.html │ │ │ ├── karma.conf.js │ │ │ └── tests-override-open.js │ │ ├── integration │ │ │ ├── travis.sh │ │ │ ├── karma-env.js │ │ │ ├── test-observable-dexie-tests.html │ │ │ └── karma.conf.js │ │ └── typings │ │ │ └── tsconfig.json │ ├── .npmignore │ ├── .gitignore │ ├── src │ │ ├── change_types.js │ │ ├── override-open.js │ │ ├── hooks │ │ │ ├── crud-monitor.js │ │ │ ├── deleting.js │ │ │ └── creating.js │ │ ├── .eslintrc.json │ │ ├── utils.js │ │ ├── on-storage.js │ │ ├── override-parse-stores-spec.js │ │ ├── wakeup-observers.js │ │ └── delete-old-changes.js │ ├── api.js │ ├── tools │ │ ├── replaceVersionAndDate.js │ │ └── build-configs │ │ │ ├── banner.txt │ │ │ ├── rollup.config.js │ │ │ ├── rollup.tests.config.js │ │ │ └── rollup.tests.unit.config.js │ ├── api.d.ts │ └── dist │ │ └── README.md └── Dexie.Syncable │ ├── test │ ├── unit │ │ ├── .gitignore │ │ ├── karma-env.js │ │ ├── .eslintrc.json │ │ ├── travis.sh │ │ ├── unit-tests-all.js │ │ ├── run-unit-tests.html │ │ ├── tests-combine-create-and-update.js │ │ ├── karma.conf.js │ │ ├── tests-register-sync-protocol.js │ │ └── tests-PersistedContext.js │ ├── integration │ │ ├── karma-env.js │ │ ├── travis.sh │ │ ├── test-syncable-dexie-tests.html │ │ └── karma.conf.js │ └── test-typings │ │ └── tsconfig.json │ ├── .npmignore │ ├── .gitignore │ ├── src │ ├── change_types.js │ ├── combine-create-and-update.js │ ├── PersistedContext.js │ ├── statuses.js │ ├── save-to-uncommitted-changes.js │ ├── .eslintrc.json │ ├── bulk-update.js │ ├── get-local-changes-for-node │ │ └── get-base-revision-and-max-client-revision.js │ ├── enqueue.js │ ├── combine-update-and-update.js │ ├── apply-changes.js │ └── merge-change.js │ ├── api.js │ ├── tools │ ├── replaceVersionAndDate.js │ └── build-configs │ │ ├── banner.txt │ │ ├── rollup.config.js │ │ └── rollup.tests.config.js │ └── dist │ └── README.md ├── .npmignore ├── tools ├── .eslintrc.json ├── build-configs │ ├── banner.txt │ ├── rollup.tests.config.js │ └── rollup.config.js ├── prepend.js ├── replaceVersionAndDate.js └── travis.sh ├── .travis.yml ├── NOTICE ├── bower.json └── dist └── README.md /samples/angular2/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/errors/index.ts: -------------------------------------------------------------------------------- 1 | export * from './errors'; 2 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | /bundle.js 2 | /bundle.js.map 3 | -------------------------------------------------------------------------------- /samples/typescript/.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /src/classes/dexie/index.ts: -------------------------------------------------------------------------------- 1 | export * from './dexie'; 2 | -------------------------------------------------------------------------------- /addons/dexie-export-import/test/index.ts: -------------------------------------------------------------------------------- 1 | import "./basic-tests"; -------------------------------------------------------------------------------- /samples/react/src/Todo.css: -------------------------------------------------------------------------------- 1 | ul li { 2 | list-style: none; 3 | } 4 | -------------------------------------------------------------------------------- /samples/typescript-simple/.gitignore: -------------------------------------------------------------------------------- 1 | /app.js 2 | /app.js.map 3 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/test/unit/.gitignore: -------------------------------------------------------------------------------- 1 | /bundle.js 2 | /bundle.js.map 3 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/test/unit/.gitignore: -------------------------------------------------------------------------------- 1 | /bundle.js 2 | /bundle.js.map 3 | -------------------------------------------------------------------------------- /addons/dexie-export-import/test/.gitignore: -------------------------------------------------------------------------------- 1 | bundle.js 2 | bundle.js.map 3 | -------------------------------------------------------------------------------- /test/integrations/dexie-relationships/index.js: -------------------------------------------------------------------------------- 1 | import "./basic-tests"; 2 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/test/unit/karma-env.js: -------------------------------------------------------------------------------- 1 | QUnit.config.autostart = false; 2 | -------------------------------------------------------------------------------- /addons/dexie-export-import/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './dexie-export-import'; 2 | -------------------------------------------------------------------------------- /test/travis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | npm run test:typings 3 | npm run test:unit 4 | -------------------------------------------------------------------------------- /samples/react-redux/src/components/Todo.css: -------------------------------------------------------------------------------- 1 | ul li { 2 | list-style: none; 3 | } 4 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/.npmignore: -------------------------------------------------------------------------------- 1 | tools/ 2 | src/ 3 | .* 4 | tmp/ 5 | **/tmp/ 6 | test 7 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/.npmignore: -------------------------------------------------------------------------------- 1 | tools/ 2 | src/ 3 | .* 4 | tmp/ 5 | **/tmp/ 6 | test 7 | -------------------------------------------------------------------------------- /addons/dexie-export-import/.npmignore: -------------------------------------------------------------------------------- 1 | tools/ 2 | src/ 3 | .* 4 | tmp/ 5 | **/tmp/ 6 | test 7 | -------------------------------------------------------------------------------- /src/classes/table/index.ts: -------------------------------------------------------------------------------- 1 | export * from './table'; 2 | export * from './table-constructor'; 3 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/.gitignore: -------------------------------------------------------------------------------- 1 | dist/*.js 2 | dist/*.map 3 | dist/*.ts 4 | dist/*.gz 5 | **/tmp/ 6 | -------------------------------------------------------------------------------- /samples/browserify/app.css: -------------------------------------------------------------------------------- 1 | textarea 2 | { 3 | width: 100%; 4 | height: 1000px; 5 | } 6 | 7 | -------------------------------------------------------------------------------- /samples/requirejs/app.css: -------------------------------------------------------------------------------- 1 | textarea 2 | { 3 | width: 100%; 4 | height: 1000px; 5 | } 6 | 7 | -------------------------------------------------------------------------------- /samples/typescript/src/app.css: -------------------------------------------------------------------------------- 1 | textarea 2 | { 3 | width: 100%; 4 | height: 1000px; 5 | } 6 | 7 | -------------------------------------------------------------------------------- /samples/react/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /samples/requirejs-with-addons/app.css: -------------------------------------------------------------------------------- 1 | textarea 2 | { 3 | width: 100%; 4 | height: 1000px; 5 | } 6 | 7 | -------------------------------------------------------------------------------- /src/classes/collection/index.ts: -------------------------------------------------------------------------------- 1 | export * from './collection'; 2 | export * from './collection-constructor'; 3 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/.gitignore: -------------------------------------------------------------------------------- 1 | dist/*.js 2 | dist/*.map 3 | dist/*.ts 4 | dist/*.gz 5 | .eslintcache 6 | **/tmp/ 7 | -------------------------------------------------------------------------------- /samples/angular2/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /samples/angular2/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmcw/Dexie.js/master/samples/angular2/src/favicon.ico -------------------------------------------------------------------------------- /samples/angular2/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /samples/react-redux/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /samples/react/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmcw/Dexie.js/master/samples/react/public/favicon.ico -------------------------------------------------------------------------------- /src/classes/transaction/index.ts: -------------------------------------------------------------------------------- 1 | export * from './transaction'; 2 | export * from './transaction-constructor'; 3 | -------------------------------------------------------------------------------- /addons/dexie-export-import/.gitignore: -------------------------------------------------------------------------------- 1 | dist/*.js 2 | dist/*.js.map 3 | dist/*.mjs 4 | dist/*.mjs.map 5 | dist/*.d.ts 6 | -------------------------------------------------------------------------------- /test/integrations/dexie-relationships/travis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | npm install 3 | npm run test:dexie-relationships 4 | -------------------------------------------------------------------------------- /samples/react-redux/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmcw/Dexie.js/master/samples/react-redux/public/favicon.ico -------------------------------------------------------------------------------- /src/public/types/then-shortcut.d.ts: -------------------------------------------------------------------------------- 1 | export type ThenShortcut = (value: T) => TResult | PromiseLike; 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | **/tmp/ 2 | samples/ 3 | addons/ 4 | *.njsproj 5 | .* 6 | *.log 7 | test/ 8 | tools/ 9 | bower.json 10 | src/ 11 | -------------------------------------------------------------------------------- /src/public/types/transaction-mode.d.ts: -------------------------------------------------------------------------------- 1 | export type TransactionMode = 'readonly' | 'readwrite' | 'r' | 'r!' | 'r?' | 'rw' | 'rw!' | 'rw?'; 2 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/src/change_types.js: -------------------------------------------------------------------------------- 1 | // Change Types 2 | export const CREATE = 1; 3 | export const UPDATE = 2; 4 | export const DELETE = 3; 5 | -------------------------------------------------------------------------------- /src/functions/is-promise-like.ts: -------------------------------------------------------------------------------- 1 | export function isPromiseLike(p): p is PromiseLike { 2 | return p && typeof p.then === 'function'; 3 | } 4 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/src/change_types.js: -------------------------------------------------------------------------------- 1 | // Change Types 2 | export const CREATE = 1; 3 | export const UPDATE = 2; 4 | export const DELETE = 3; 5 | -------------------------------------------------------------------------------- /src/public/types/db-schema.d.ts: -------------------------------------------------------------------------------- 1 | import { TableSchema } from "./table-schema"; 2 | 3 | export type DbSchema = {[tableName: string]: TableSchema}; 4 | -------------------------------------------------------------------------------- /test/karma-env.js: -------------------------------------------------------------------------------- 1 | QUnit.config.autostart = false; 2 | window.workerImports = ['../dist/dexie.js']; 3 | window.workerSource = 'base/test/worker.js'; 4 | -------------------------------------------------------------------------------- /src/public/types/dexie-dom-dependencies.d.ts: -------------------------------------------------------------------------------- 1 | export interface DexieDOMDependencies { 2 | indexedDB: IDBFactory; 3 | IDBKeyRange: typeof IDBKeyRange; 4 | } 5 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/api.js: -------------------------------------------------------------------------------- 1 | // This file is deliberatly left empty to allow the api.d.ts to contain the definitions for Dexie.Syncable without generating an error on webpack 2 | -------------------------------------------------------------------------------- /samples/react/src/db.js: -------------------------------------------------------------------------------- 1 | import Dexie from 'dexie'; 2 | 3 | const db = new Dexie('ReactSampleDB'); 4 | db.version(1).stores({ todos: '++id' }); 5 | 6 | export default db; 7 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/api.js: -------------------------------------------------------------------------------- 1 | // This file is deliberatly left empty to allow the api.d.ts to contain the definitions for Dexie.Observable without generating an error on webpack 2 | -------------------------------------------------------------------------------- /samples/react-redux/src/db.js: -------------------------------------------------------------------------------- 1 | import Dexie from 'dexie'; 2 | 3 | const db = new Dexie('ReactReduxSampleDB'); 4 | db.version(1).stores({ todos: '++id' }); 5 | 6 | export default db; 7 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/test/integration/travis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | echo "Installing dependencies for dexie-observable" 3 | npm install >/dev/null 4 | npm run build 5 | npm run test:integration 6 | -------------------------------------------------------------------------------- /samples/typescript-simple/app-init.js: -------------------------------------------------------------------------------- 1 | require.config({ 2 | baseUrl: "/", 3 | paths: { 4 | "dexie": "node_modules/dexie/dist/dexie" 5 | } 6 | }); 7 | 8 | requirejs(['./app']); 9 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/test/unit/travis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | echo "Installing dependencies for dexie-observable" 3 | npm install >/dev/null 4 | npm run build 5 | npm run test:typings 6 | npm run test:unit 7 | -------------------------------------------------------------------------------- /samples/react-redux/src/constants.js: -------------------------------------------------------------------------------- 1 | export const ADD_TODO = 'ADD_TODO'; 2 | export const UPDATE_TODO = 'UPDATE_TODO'; 3 | export const DELETE_TODO = 'DELETE_TODO'; 4 | export const LOAD_TODOS = 'LOAD_TODOS'; 5 | -------------------------------------------------------------------------------- /samples/typescript-simple/README.md: -------------------------------------------------------------------------------- 1 | ## Build 2 | ``` 3 | npm install 4 | npm run build 5 | ``` 6 | 7 | ## Run 8 | ``` 9 | npm run start 10 | ``` 11 | Launch a web browser to http://localhost:8081 12 | -------------------------------------------------------------------------------- /src/public/types/dexie-event.d.ts: -------------------------------------------------------------------------------- 1 | export interface DexieEvent { 2 | subscribers: Function[]; 3 | fire(...args:any[]): any; 4 | subscribe(fn: (...args:any[]) => any): void; 5 | unsubscribe(fn: (...args:any[]) => any): void; 6 | } 7 | -------------------------------------------------------------------------------- /samples/react/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import './index.css'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | -------------------------------------------------------------------------------- /src/public/types/version.d.ts: -------------------------------------------------------------------------------- 1 | import { Transaction } from "./transaction"; 2 | 3 | export interface Version { 4 | stores(schema: { [tableName: string]: string | null }): Version; 5 | upgrade(fn: (trans: Transaction) => void): Version; 6 | } 7 | -------------------------------------------------------------------------------- /samples/typescript-simple/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "amd", 4 | "target": "es5", 5 | "moduleResolution": "node", 6 | "sourceMap": true, 7 | "lib": ["dom", "es2015"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/functions/combine.ts: -------------------------------------------------------------------------------- 1 | export function combine(filter1, filter2) { 2 | return filter1 ? 3 | filter2 ? 4 | function () { return filter1.apply(this, arguments) && filter2.apply(this, arguments); } : 5 | filter1 : 6 | filter2; 7 | } 8 | -------------------------------------------------------------------------------- /samples/angular2/src/app/core/dexie.service.ts: -------------------------------------------------------------------------------- 1 | import Dexie from 'dexie'; 2 | 3 | export class DexieService extends Dexie { 4 | constructor() { 5 | super('Ng2DexieSample'); 6 | this.version(1).stores({ 7 | todos: '++id', 8 | }); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /samples/react/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # testing 7 | coverage 8 | 9 | # production 10 | build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log 16 | -------------------------------------------------------------------------------- /samples/react-redux/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # testing 7 | coverage 8 | 9 | # production 10 | build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log 16 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/test/unit/unit-tests-all.js: -------------------------------------------------------------------------------- 1 | import './hooks/tests-creating.js'; 2 | import './hooks/tests-deleting.js'; 3 | import './hooks/tests-updating.js'; 4 | import './tests-override-open.js'; 5 | import './tests-override-parse-stores-spec.js'; 6 | import './tests-observable-misc'; 7 | -------------------------------------------------------------------------------- /samples/requirejs/README.md: -------------------------------------------------------------------------------- 1 | # Sample - Using Dexie.js with Browserify 2 | 3 | This is a sample on how to use browserify to include dexie.js into your web project. 4 | 5 | npm install http-server -g 6 | http-server ../.. -a localhost -p 8081 7 | Surf to http://localhost:8081/samples/requirejs/app.html 8 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/test/integration/karma-env.js: -------------------------------------------------------------------------------- 1 | // workerImports will be used by tests-open.js in the dexie test suite when 2 | // launching a Worker. This line will instruct the worker to import dexie-observable. 3 | window.workerImports.push("../addons/Dexie.Observable/dist/dexie-observable.js"); 4 | -------------------------------------------------------------------------------- /src/functions/compare-functions.ts: -------------------------------------------------------------------------------- 1 | import { IndexableType } from '../public/types/indexable-type'; 2 | 3 | export function simpleCompare(a, b) { 4 | return a < b ? -1 : a === b ? 0 : 1; 5 | } 6 | 7 | export function simpleCompareReverse(a, b) { 8 | return a > b ? -1 : a === b ? 0 : 1; 9 | } 10 | -------------------------------------------------------------------------------- /src/public/types/index-spec.d.ts: -------------------------------------------------------------------------------- 1 | export interface IndexSpec { 2 | name: string; 3 | keyPath: string | Array | undefined; 4 | unique: boolean | undefined; 5 | multi: boolean | undefined; 6 | auto: boolean | undefined; 7 | compound: boolean | undefined; 8 | src: string; 9 | } 10 | -------------------------------------------------------------------------------- /samples/typescript-simple/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Typescript simple sample with requirejs 4 | 5 | 6 | 7 | Alerts should show. If not, press F12 to debug. 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/public/types/table-schema.d.ts: -------------------------------------------------------------------------------- 1 | import { IndexSpec } from "./index-spec"; 2 | 3 | export interface TableSchema { 4 | name: string; 5 | primKey: IndexSpec; 6 | indexes: IndexSpec[]; 7 | mappedClass: Function; 8 | idxByName: {[name: string]: IndexSpec}; 9 | readHook?: (x:any) => any 10 | } 11 | -------------------------------------------------------------------------------- /samples/react/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 40px; 13 | padding: 20px; 14 | margin-bottom: 40px; 15 | color: white; 16 | } 17 | -------------------------------------------------------------------------------- /tools/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 6, 4 | "sourceType": "module", 5 | "ecmaFeatures": { 6 | } 7 | }, 8 | "env": { 9 | "node": true 10 | }, 11 | "rules": { 12 | "no-undef": ["error"] 13 | }, 14 | "globals": { 15 | "Promise": true 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /samples/angular2/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /samples/react-redux/src/components/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 40px; 13 | padding: 20px; 14 | margin-bottom: 40px; 15 | color: white; 16 | } 17 | -------------------------------------------------------------------------------- /samples/requirejs-with-addons/README.md: -------------------------------------------------------------------------------- 1 | # Sample - Using Dexie.js + addons with RequireJS 2 | 3 | This is a sample on how to use RequireJS to include dexie.js with addons into your web project. 4 | 5 | npm install http-server -g 6 | http-server ../.. -a localhost -p 8081 7 | Surf to http://localhost:8081/samples/requirejs-with-addons/app.html 8 | -------------------------------------------------------------------------------- /src/public/types/middleware.d.ts: -------------------------------------------------------------------------------- 1 | import { DBCore } from "./dbcore"; 2 | 3 | export interface Middleware { 4 | stack: TStack["stack"], 5 | create: (down: TStack) => Partial; 6 | level?: number; 7 | name?: string; 8 | } 9 | 10 | export interface DexieStacks { 11 | dbcore: DBCore; 12 | } 13 | -------------------------------------------------------------------------------- /samples/requirejs/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Demo how to include Dexie with RequireJS 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /test/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 6, 4 | "sourceType": "module", 5 | "ecmaFeatures": { 6 | } 7 | }, 8 | "env": { 9 | "browser": true, 10 | "node": true 11 | }, 12 | "rules": { 13 | "no-undef": ["error"] 14 | }, 15 | "globals": { 16 | "Promise": true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /samples/requirejs-with-addons/scripts/ChangeLogger.js: -------------------------------------------------------------------------------- 1 | 2 | define(['./db', './console'], function (db, console) { 3 | 4 | function ChangeLogger() { 5 | db.on('changes', function(changes) { 6 | console.log("Changes: " + JSON.stringify(changes, null, 4)); 7 | }); 8 | } 9 | 10 | return ChangeLogger; 11 | }); 12 | -------------------------------------------------------------------------------- /samples/requirejs-with-addons/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Demo how to include Dexie + addons with RequireJS 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/test/unit/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 6, 4 | "sourceType": "module", 5 | "ecmaFeatures": { 6 | } 7 | }, 8 | "env": { 9 | "browser": true, 10 | "node": true 11 | }, 12 | "rules": { 13 | "no-undef": ["error"] 14 | }, 15 | "globals": { 16 | "Promise": true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/test/unit/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 6, 4 | "sourceType": "module", 5 | "ecmaFeatures": { 6 | } 7 | }, 8 | "env": { 9 | "browser": true, 10 | "node": true 11 | }, 12 | "rules": { 13 | "no-undef": ["error"] 14 | }, 15 | "globals": { 16 | "Promise": true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /addons/dexie-export-import/test/travis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | echo "Installing dependencies for dexie-export-import" 3 | npm install >/dev/null 4 | npm run build 5 | # This test fails sporadically on Safari 12. Needs to retry it if it fails. 6 | n=1 7 | until [ $n -ge 4 ] 8 | do 9 | echo "Retry $n of 3" 10 | npm run test && exit 0 11 | n=$[$n+1] 12 | done 13 | exit 1 14 | -------------------------------------------------------------------------------- /samples/browserify/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Demo how to include Dexie with Browserify 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /tools/build-configs/banner.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * Dexie.js - a minimalistic wrapper for IndexedDB 3 | * =============================================== 4 | * 5 | * By David Fahlander, david.fahlander@gmail.com 6 | * 7 | * Version {version}, {date} 8 | * 9 | * http://dexie.org 10 | * 11 | * Apache License Version 2.0, January 2004, http://www.apache.org/licenses/ 12 | */ 13 | -------------------------------------------------------------------------------- /samples/angular2/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Ng2dexie 6 | 7 | 8 | 9 | 10 | 11 | 12 | Loading... 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/public/types/indexable-type.d.ts: -------------------------------------------------------------------------------- 1 | export type IndexableTypePart = 2 | string | number | Date | ArrayBuffer | ArrayBufferView | DataView | Array>; 3 | 4 | export type IndexableTypeArray = Array; 5 | export type IndexableTypeArrayReadonly = ReadonlyArray; 6 | export type IndexableType = IndexableTypePart | IndexableTypeArrayReadonly; 7 | 8 | -------------------------------------------------------------------------------- /tools/prepend.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const [file, prepentionFile] = process.argv.slice(2); 3 | 4 | const prepention = fs.readFileSync(prepentionFile); 5 | const fileContent = fs.readFileSync(file); 6 | 7 | const fd = fs.openSync(file, 'w'); 8 | try { 9 | fs.writeSync(fd, prepention); 10 | fs.writeSync(fd, fileContent); 11 | } finally { 12 | fs.closeSync(fd); 13 | } 14 | -------------------------------------------------------------------------------- /samples/browserify/README.md: -------------------------------------------------------------------------------- 1 | # Sample - Using Dexie.js with Browserify 2 | 3 | This is a sample on how to use browserify to include dexie.js into your web project. 4 | 5 | ## Install 6 | npm install 7 | 8 | ## Build 9 | browserify scripts/main.js -o bundle.js 10 | 11 | ## Run 12 | npm install -g http-server 13 | http-server . -a localhost -p 8081 14 | Surf to http://localhost:8081/app.html 15 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/test/integration/karma-env.js: -------------------------------------------------------------------------------- 1 | // workerImports will be used by tests-open.js in the dexie test suite when 2 | // launching a Worker. This line will instruct the worker to import dexie-observable 3 | // and dexie-syncable. 4 | window.workerImports.push("../addons/Dexie.Observable/dist/dexie-observable.js"); 5 | window.workerImports.push("../addons/Dexie.Syncable/dist/dexie-syncable.js"); 6 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/test/integration/travis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | cd ../../../Dexie.Observable 3 | echo "Installing dependencies for dexie-observable" 4 | npm install >/dev/null 5 | echo "Building dexie-observable" 6 | npm run build 7 | cd - 8 | echo "Installing dependencies for dexie-syncable" 9 | npm install >/dev/null 10 | echo "Building dexie-syncable" 11 | npm run build 12 | 13 | npm run test:integration 14 | -------------------------------------------------------------------------------- /samples/react-redux/src/store/index.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux'; 2 | import thunk from 'redux-thunk'; 3 | 4 | import rootReducer from '../reducers'; 5 | 6 | const initialState = { 7 | todos: [], 8 | }; 9 | 10 | const store = createStore( 11 | rootReducer, 12 | initialState, 13 | compose( 14 | applyMiddleware(thunk) 15 | ), 16 | ); 17 | 18 | export default store; 19 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/test/typings/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "es6", 4 | "target": "es5", 5 | "noImplicitAny": true, 6 | "strictNullChecks": true, 7 | "outDir": "../../tools/tmp/test-typings", 8 | "moduleResolution": "node", 9 | "lib": ["dom", "es2015"] 10 | }, 11 | "files": [ 12 | "test-typings.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/test/test-typings/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "es6", 4 | "target": "es5", 5 | "noImplicitAny": true, 6 | "strictNullChecks": true, 7 | "outDir": "../../tools/tmp/test-typings", 8 | "moduleResolution": "node", 9 | "lib": ["dom", "es2015"] 10 | }, 11 | "files": [ 12 | "test-typings.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/test/unit/travis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | cd ../../../Dexie.Observable 3 | echo "Installing dependencies for dexie-observable" 4 | npm install >/dev/null 5 | echo "Building dexie-observable" 6 | npm run build 7 | cd - 8 | echo "Installing dependencies for dexie-syncable" 9 | npm install >/dev/null 10 | echo "Building dexie-syncable" 11 | npm run build 12 | 13 | npm run test:typings 14 | npm run test:unit 15 | -------------------------------------------------------------------------------- /samples/angular2/src/main.ts: -------------------------------------------------------------------------------- 1 | import './polyfills.ts'; 2 | 3 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 4 | import { enableProdMode } from '@angular/core'; 5 | import { environment } from './environments/environment'; 6 | import { AppModule } from './app/app.module'; 7 | 8 | if (environment.production) { 9 | enableProdMode(); 10 | } 11 | 12 | platformBrowserDynamic().bootstrapModule(AppModule); 13 | -------------------------------------------------------------------------------- /samples/electron/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-electron", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "main.js", 6 | "scripts": { 7 | "start": "node_modules/.bin/electron .", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "dexie": "^1.4.0-rc.1", 14 | "electron-prebuilt": "^1.2.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/typings-test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "es6", 4 | "target": "es5", 5 | "noImplicitAny": true, 6 | "strictNullChecks": true, 7 | "noImplicitThis": true, 8 | "outDir": "../../tools/tmp/test-typings", 9 | "moduleResolution": "node", 10 | "lib": ["dom", "es2015"] 11 | }, 12 | "files": [ 13 | "test-typings.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /tools/replaceVersionAndDate.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const files = process.argv.slice(2); 3 | const version = require('../package.json').version; 4 | 5 | files.forEach(file => { 6 | let fileContent = fs.readFileSync(file, "utf-8"); 7 | fileContent = fileContent 8 | .replace(/{version}/g, version) 9 | .replace(/{date}/g, new Date().toDateString()); 10 | fs.writeFileSync(file, fileContent, "utf-8"); 11 | }); 12 | -------------------------------------------------------------------------------- /samples/angular2/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "declaration": false, 5 | "emitDecoratorMetadata": true, 6 | "experimentalDecorators": true, 7 | "module": "commonjs", 8 | "moduleResolution": "node", 9 | "outDir": "../dist/out-tsc-e2e", 10 | "sourceMap": true, 11 | "target": "es5", 12 | "typeRoots": [ 13 | "../node_modules/@types" 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /samples/angular2/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `angular-cli.json`. 5 | 6 | export const environment = { 7 | production: false 8 | }; 9 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/tools/replaceVersionAndDate.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const files = process.argv.slice(2); 3 | const version = require('../package.json').version; 4 | 5 | files.forEach(file => { 6 | let fileContent = fs.readFileSync(file, "utf-8"); 7 | fileContent = fileContent 8 | .replace(/{version}/g, version) 9 | .replace(/{date}/g, new Date().toDateString()); 10 | fs.writeFileSync(file, fileContent, "utf-8"); 11 | }); 12 | -------------------------------------------------------------------------------- /samples/typescript/src/console.ts: -------------------------------------------------------------------------------- 1 | export default class Console { 2 | textarea: HTMLTextAreaElement; 3 | 4 | constructor() { 5 | this.textarea = document.createElement('textarea'); 6 | } 7 | 8 | log(txt: string, type?: string) { 9 | if (type) this.textarea.value += type + " "; 10 | this.textarea.value += txt + "\n"; 11 | } 12 | error = function(txt: string) { 13 | this.log(txt, "ERROR!"); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/tools/replaceVersionAndDate.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const files = process.argv.slice(2); 3 | const version = require('../package.json').version; 4 | 5 | files.forEach(file => { 6 | let fileContent = fs.readFileSync(file, "utf-8"); 7 | fileContent = fileContent 8 | .replace(/{version}/g, version) 9 | .replace(/{date}/g, new Date().toDateString()); 10 | fs.writeFileSync(file, fileContent, "utf-8"); 11 | }); 12 | -------------------------------------------------------------------------------- /samples/typescript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "es6", 4 | "target": "es5", 5 | "noImplicitAny": true, 6 | "removeComments": false, 7 | "sourceMap": true, 8 | "outDir": "out/ts", 9 | "moduleResolution": "node" 10 | }, 11 | "files": [ 12 | "node_modules/typescript/lib/lib.es6.d.ts", 13 | "src/console.ts", 14 | "src/appdb.ts", 15 | "src/app.ts" 16 | ] 17 | } -------------------------------------------------------------------------------- /src/public/types/transaction-events.d.ts: -------------------------------------------------------------------------------- 1 | import { DexieEvent } from "./dexie-event"; 2 | import { DexieEventSet } from "./dexie-event-set"; 3 | 4 | export interface TransactionEvents extends DexieEventSet { 5 | (eventName: 'complete', subscriber: () => any): void; 6 | (eventName: 'abort', subscriber: () => any): void; 7 | (eventName: 'error', subscriber: (error:any) => any): void; 8 | complete: DexieEvent; 9 | abort: DexieEvent; 10 | error: DexieEvent; 11 | } 12 | -------------------------------------------------------------------------------- /samples/angular2/src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "", 4 | "declaration": false, 5 | "emitDecoratorMetadata": true, 6 | "experimentalDecorators": true, 7 | "lib": ["es6", "dom"], 8 | "mapRoot": "./", 9 | "module": "es6", 10 | "moduleResolution": "node", 11 | "outDir": "../dist/out-tsc", 12 | "sourceMap": true, 13 | "target": "es5", 14 | "typeRoots": [ 15 | "../node_modules/@types" 16 | ] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/dbcore/keyrange.ts: -------------------------------------------------------------------------------- 1 | import { KeyRange, RangeType } from '../public/types/dbcore'; 2 | 3 | var x: KeyRange; 4 | 5 | export const AnyRange: KeyRange = { 6 | type: RangeType.Any, 7 | lower: -Infinity, 8 | lowerOpen: false, 9 | upper: [[]], // BUGBUG: depends on indexeddb impl. 10 | upperOpen: false 11 | } 12 | 13 | export const NeverRange: KeyRange = { 14 | type: RangeType.Never, 15 | lower: -Infinity, 16 | lowerOpen: true, 17 | upper: -Infinity, 18 | upperOpen: true 19 | } 20 | -------------------------------------------------------------------------------- /src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "target": "es5", 5 | "module": "es2015", 6 | "importHelpers": true, 7 | "strictNullChecks": false, 8 | "noImplicitAny": false, 9 | "noImplicitReturns": false, 10 | "moduleResolution": "node", 11 | "lib": ["es2015", "dom"], 12 | "forceConsistentCasingInFileNames": true, 13 | "outDir": "../tools/tmp/src/", 14 | "sourceMap": true 15 | }, 16 | "dirs": [ 17 | "." 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/src/combine-create-and-update.js: -------------------------------------------------------------------------------- 1 | import Dexie from 'dexie'; 2 | 3 | export default function combineCreateAndUpdate(prevChange, nextChange) { 4 | var clonedChange = Dexie.deepClone(prevChange); // Clone object before modifying since the earlier change in db.changes[] would otherwise be altered. 5 | Object.keys(nextChange.mods).forEach(function (keyPath) { 6 | Dexie.setByKeyPath(clonedChange.obj, keyPath, nextChange.mods[keyPath]); 7 | }); 8 | return clonedChange; 9 | } 10 | -------------------------------------------------------------------------------- /samples/angular2/src/app/app-add-todo.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Output, EventEmitter } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-add-todo', 5 | template: ` 6 | 7 | 8 | `, 9 | }) 10 | export class AddTodoComponent { 11 | @Output() addTodo = new EventEmitter(); 12 | title = ''; 13 | 14 | onAddTodo() { 15 | this.addTodo.emit(this.title); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /samples/typescript/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from 'rollup-plugin-node-resolve'; 2 | import commonjs from 'rollup-plugin-commonjs'; 3 | 4 | export default { 5 | entry: 'out/ts/app.js', 6 | dest: 'out/bundle.js', 7 | format: 'iife', 8 | sourceMap: true, 9 | onwarn: function ( message ) { 10 | if ( /The 'this' keyword is equivalent to 'undefined' at the top level of an ES module/.test( message ) ) return; 11 | console.warn(message); 12 | }, 13 | plugins: [resolve(), commonjs()] 14 | }; 15 | -------------------------------------------------------------------------------- /samples/browserify/scripts/console.js: -------------------------------------------------------------------------------- 1 | function Console() { 2 | this.textarea = document.createElement('textarea'); 3 | this.log = function (txt, type) { 4 | if (type) this.textarea.value += type + " "; 5 | this.textarea.value += txt + "\n"; 6 | } 7 | this.error = function (txt) { 8 | this.log(txt, "ERROR!"); 9 | } 10 | } 11 | 12 | var console = new Console(); 13 | 14 | document.getElementById('consoleArea').appendChild(console.textarea); 15 | 16 | module.exports = console; 17 | -------------------------------------------------------------------------------- /src/public/types/dexie-event-set.d.ts: -------------------------------------------------------------------------------- 1 | import { DexieEvent } from "./dexie-event"; 2 | 3 | export interface DexieEventSet { 4 | (eventName: string): DexieEvent; // To be able to unsubscribe. 5 | 6 | addEventType ( 7 | eventName: string, 8 | chainFunction?: (f1:Function,f2:Function)=>Function, 9 | defaultFunction?: Function): DexieEvent; 10 | addEventType ( 11 | events: {[eventName:string]: ('asap' | [(f1:Function,f2:Function)=>Function, Function])}) 12 | : DexieEvent; 13 | } 14 | -------------------------------------------------------------------------------- /addons/dexie-export-import/test/qunit.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'qunit' { 2 | function module(name: string, options?: { 3 | setup?: () => void; 4 | teardown?: () => void 5 | }); 6 | function asyncTest(name: string, fn: ()=>void); 7 | function start(); 8 | function stop(); 9 | function strictEqual(a: any, b: any, description: string); 10 | function deepEqual(a: any, b: any, description: string); 11 | function equal(a: any, b: any, description: string); 12 | function ok(x: any, description: string); 13 | } 14 | -------------------------------------------------------------------------------- /addons/dexie-export-import/src/tson-arraybuffer.ts: -------------------------------------------------------------------------------- 1 | import Typeson from 'typeson'; 2 | import {encode, decode} from 'base64-arraybuffer-es6'; 3 | 4 | export default { 5 | arraybuffer: { 6 | test (x) { return Typeson.toStringTag(x) === 'ArrayBuffer'; }, 7 | replace (b) { 8 | return encode(b, 0, b.byteLength); 9 | }, 10 | revive (b64) { 11 | const buffer = decode(b64); 12 | return buffer; 13 | } 14 | } 15 | }; 16 | 17 | // See also typed-arrays! 18 | -------------------------------------------------------------------------------- /samples/react-redux/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { Provider } from 'react-redux'; 4 | 5 | import {loadTodos} from './actions'; 6 | import store from './store'; 7 | import {LOAD_TODOS} from './constants'; 8 | 9 | import App from './containers/App'; 10 | import './index.css'; 11 | 12 | store.dispatch(loadTodos(LOAD_TODOS)); 13 | 14 | ReactDOM.render( 15 | 16 | 17 | , 18 | document.getElementById('root'), 19 | ); 20 | -------------------------------------------------------------------------------- /samples/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-sample", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "react-scripts": "^1.1.5" 7 | }, 8 | "dependencies": { 9 | "dexie": "^2.0.4", 10 | "prop-types": "^15.6.1", 11 | "react": "^16.4.1", 12 | "react-dom": "^16.4.1" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "react-scripts build", 17 | "test": "react-scripts test --env=jsdom", 18 | "eject": "react-scripts eject" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "target": "es5", 5 | "module": "es2015", 6 | "importHelpers": true, 7 | "strictNullChecks": false, 8 | "noImplicitAny": false, 9 | "noImplicitReturns": false, 10 | "moduleResolution": "node", 11 | "lib": ["es2015", "dom"], 12 | "forceConsistentCasingInFileNames": true, 13 | "outDir": "../tools/tmp/test/", 14 | "sourceMap": true, 15 | "rootDir": "." 16 | }, 17 | "files": [ 18 | "tests-all.js" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /test/karma.conf.js: -------------------------------------------------------------------------------- 1 | const {karmaCommon, getKarmaConfig, defaultBrowserMatrix} = require('./karma.common'); 2 | 3 | module.exports = function (config) { 4 | const cfg = getKarmaConfig (defaultBrowserMatrix, { 5 | // Base path should point at the root 6 | basePath: '..', 7 | // Files to include 8 | files: karmaCommon.files.concat([ 9 | 'dist/dexie.js', 10 | 'test/bundle.js', 11 | { watched: true, included: false, served: true, pattern: 'test/worker.js' }, 12 | ]) 13 | }); 14 | 15 | config.set(cfg); 16 | } 17 | -------------------------------------------------------------------------------- /src/helpers/table-schema.ts: -------------------------------------------------------------------------------- 1 | import { IndexSpec } from '../public/types/index-spec'; 2 | import { TableSchema } from '../public/types/table-schema'; 3 | import { createIndexSpec } from './index-spec'; 4 | import { arrayToObject } from '../functions/utils'; 5 | 6 | export function createTableSchema ( 7 | name: string, 8 | primKey: IndexSpec, 9 | indexes: IndexSpec[] 10 | ): TableSchema { 11 | return { 12 | name, 13 | primKey, 14 | indexes, 15 | mappedClass: null, 16 | idxByName: arrayToObject(indexes, index => [index.name, index]) 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /addons/dexie-export-import/test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "es2015", 5 | "importHelpers": true, 6 | "strictNullChecks": true, 7 | "noImplicitAny": false, 8 | "noImplicitReturns": true, 9 | "moduleResolution": "node", 10 | "lib": ["es2015", "dom"], 11 | "forceConsistentCasingInFileNames": true, 12 | "outDir": "../tools/tmp/", 13 | "sourceMap": true, 14 | "rootDir": ".." 15 | }, 16 | "files": [ 17 | "../src/dexie-export-import.ts", 18 | "index.ts", 19 | "qunit.d.ts" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/src/PersistedContext.js: -------------------------------------------------------------------------------- 1 | import Dexie from 'dexie'; 2 | 3 | export default function initPersistedContext(node) { 4 | // 5 | // PersistedContext : IPersistedContext 6 | // 7 | return class PersistedContext { 8 | constructor(nodeID, otherProps) { 9 | this.nodeID = nodeID; 10 | if (otherProps) Dexie.extend(this, otherProps); 11 | } 12 | 13 | save() { 14 | // Store this instance in the syncContext property of the node it belongs to. 15 | return Dexie.vip(() => { 16 | return node.save(); 17 | }); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /samples/requirejs/scripts/console.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 3 | function Console() { 4 | this.textarea = document.createElement('textarea'); 5 | this.log = function (txt, type) { 6 | if (type) this.textarea.value += type + " "; 7 | this.textarea.value += txt + "\n"; 8 | } 9 | this.error = function (txt) { 10 | this.log(txt, "ERROR!"); 11 | } 12 | } 13 | 14 | var console = new Console(); 15 | 16 | document.getElementById('consoleArea').appendChild(console.textarea); 17 | 18 | return console; 19 | }); 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | addons: 3 | firefox: "47.0" 4 | node_js: 5 | - "8" 6 | matrix: 7 | fast_finish: true 8 | before_script: 9 | - export DISPLAY=:99.0 10 | - sh -e /etc/init.d/xvfb start 11 | env: 12 | - TF=test 13 | - TF=addons/Dexie.Observable/test/unit 14 | - TF=addons/Dexie.Observable/test/integration 15 | - TF=addons/Dexie.Syncable/test/unit 16 | - TF=addons/Dexie.Syncable/test/integration 17 | - TF=addons/dexie-export-import/test 18 | - TF=test/integrations/dexie-relationships 19 | script: 20 | - npm run build 21 | - cd $TF 22 | - pwd 23 | - bash ./travis.sh 24 | -------------------------------------------------------------------------------- /addons/dexie-export-import/src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "es2015", 5 | "declaration": true, 6 | "importHelpers": true, 7 | "strictNullChecks": true, 8 | "noImplicitAny": false, 9 | "noImplicitReturns": true, 10 | "moduleResolution": "node", 11 | "lib": ["es2015", "dom"], 12 | "forceConsistentCasingInFileNames": true, 13 | "outDir": "../tools/tmp/src/", 14 | "declarationDir": "../dist/", 15 | "sourceMap": true, 16 | "rootDir": "." 17 | }, 18 | "files": [ 19 | "dexie-export-import.ts" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /samples/react-redux/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-redux-sample", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "react-scripts": "0.8.4" 7 | }, 8 | "dependencies": { 9 | "dexie": "^2.0.0-beta.7", 10 | "react": "^15.4.1", 11 | "react-dom": "^15.4.1", 12 | "react-redux": "^5.0.1", 13 | "redux": "^3.6.0", 14 | "redux-thunk": "^2.1.0" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test --env=jsdom", 20 | "eject": "react-scripts eject" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /samples/requirejs-with-addons/scripts/console.js: -------------------------------------------------------------------------------- 1 | 2 | define(function () { 3 | 4 | function Console() { 5 | this.textarea = document.createElement('textarea'); 6 | this.log = function (txt, type) { 7 | if (type) this.textarea.value += type + " "; 8 | this.textarea.value += txt + "\n"; 9 | } 10 | this.error = function (txt) { 11 | this.log(txt, "ERROR!"); 12 | } 13 | } 14 | 15 | var console = new Console(); 16 | 17 | document.getElementById('consoleArea').appendChild(console.textarea); 18 | 19 | return console; 20 | }); 21 | -------------------------------------------------------------------------------- /src/functions/quirks.ts: -------------------------------------------------------------------------------- 1 | import { maxString } from '../globals/constants'; 2 | 3 | export function safariMultiStoreFix(storeNames: string[]) { 4 | return storeNames.length === 1 ? storeNames[0] : storeNames; 5 | } 6 | 7 | export function getNativeGetDatabaseNamesFn(indexedDB) { 8 | var fn = indexedDB && (indexedDB.getDatabaseNames || indexedDB.webkitGetDatabaseNames); 9 | return fn && fn.bind(indexedDB); 10 | } 11 | 12 | export function getMaxKey (IdbKeyRange: typeof IDBKeyRange) { 13 | try { 14 | IdbKeyRange.only([[]]); 15 | return [[]]; 16 | } catch (e) { 17 | return maxString; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/functions/stringify-key.d.ts: -------------------------------------------------------------------------------- 1 | import { IndexableType } from "../public/types/indexable-type"; 2 | 3 | /** Makes any IndexableType instance a string that is unique within it's own 4 | * type range and satisfies the equality algorithm indexedDB keys, for example 5 | * stringifyKey(new Date(1230000000)) === stringifyKey(new Date(1230000000)) 6 | * stringifyKey(new Uint8Array([1,2,3])) === stringifyKey(new Uint8Array([1,2,3])) 7 | * 8 | * @param key {IndexableType} Key to string 9 | */ 10 | export function stringifyKey (key: IndexableType): string; 11 | export function unstringifyKey (str: string): IndexableType; 12 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Dexie.js 2 | 3 | Copyright (c) 2014-2017 David Fahlander 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | -------------------------------------------------------------------------------- /samples/typescript/src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Demo how to include Dexie with typescript 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /src/public/types/transaction.d.ts: -------------------------------------------------------------------------------- 1 | import { Table } from "./table"; 2 | import { Database } from "./database"; 3 | import { TransactionEvents } from "./transaction-events"; 4 | 5 | export interface Transaction { 6 | db: Database; 7 | active: boolean; 8 | mode: IDBTransactionMode; 9 | //tables: { [type: string]: Table }; Deprecated since 2.0. Obsolete from v3.0. 10 | storeNames: Array; 11 | parent?: Transaction; 12 | on: TransactionEvents; 13 | abort(): void; 14 | table(tableName: string): Table; 15 | table(tableName: string): Table; 16 | table(tableName: string): Table; 17 | } 18 | -------------------------------------------------------------------------------- /addons/dexie-export-import/src/json-structure.ts: -------------------------------------------------------------------------------- 1 | export const VERSION = 1; 2 | 3 | export interface DexieExportJsonStructure { 4 | formatName: 'dexie'; 5 | formatVersion: typeof VERSION; 6 | data: { 7 | databaseName: string; 8 | databaseVersion: number; 9 | tables: Array<{ 10 | name: string; 11 | schema: string; 12 | rowCount: number; 13 | }>; 14 | data: Array<{ 15 | tableName: string; 16 | inbound: boolean; 17 | rows: any[]; 18 | }>; 19 | } 20 | } 21 | 22 | export type DexieExportedDatabase = DexieExportJsonStructure["data"]; 23 | export type DexieExportedTable = DexieExportedDatabase["data"][number]; 24 | -------------------------------------------------------------------------------- /addons/dexie-export-import/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | dexie-export-import unit tests 6 | 7 | 8 | 9 |
10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /addons/dexie-export-import/tools/build-configs/banner.txt: -------------------------------------------------------------------------------- 1 | /* ========================================================================== 2 | * dexie-export-import.js 3 | * ========================================================================== 4 | * 5 | * Dexie addon for exporting and importing databases to / from Blobs. 6 | * 7 | * By David Fahlander, david.fahlander@gmail.com, 8 | * 9 | * ========================================================================== 10 | * 11 | * Version {version}, {date} 12 | * 13 | * http://dexie.org 14 | * 15 | * Apache License Version 2.0, January 2004, http://www.apache.org/licenses/ 16 | * 17 | */ 18 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/test/unit/run-unit-tests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Dexie.Observable Unit tests 6 | 7 | 8 | 9 |
10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /samples/angular2/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | 7 | # dependencies 8 | /node_modules 9 | 10 | # IDEs and editors 11 | /.idea 12 | .project 13 | .classpath 14 | .c9/ 15 | *.launch 16 | .settings/ 17 | 18 | # IDE - VSCode 19 | .vscode/* 20 | !.vscode/settings.json 21 | !.vscode/tasks.json 22 | !.vscode/launch.json 23 | !.vscode/extensions.json 24 | 25 | # misc 26 | /.sass-cache 27 | /connect.lock 28 | /coverage/* 29 | /libpeerconnection.log 30 | npm-debug.log 31 | testem.log 32 | /typings 33 | 34 | # e2e 35 | /e2e/*.js 36 | /e2e/*.map 37 | 38 | #System Files 39 | .DS_Store 40 | Thumbs.db 41 | -------------------------------------------------------------------------------- /samples/angular2/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | // This file includes polyfills needed by Angular 2 and is loaded before 2 | // the app. You can add your own extra polyfills to this file. 3 | import 'core-js/es6/symbol'; 4 | import 'core-js/es6/object'; 5 | import 'core-js/es6/function'; 6 | import 'core-js/es6/parse-int'; 7 | import 'core-js/es6/parse-float'; 8 | import 'core-js/es6/number'; 9 | import 'core-js/es6/math'; 10 | import 'core-js/es6/string'; 11 | import 'core-js/es6/date'; 12 | import 'core-js/es6/array'; 13 | import 'core-js/es6/regexp'; 14 | import 'core-js/es6/map'; 15 | import 'core-js/es6/set'; 16 | import 'core-js/es6/reflect'; 17 | 18 | import 'core-js/es7/reflect'; 19 | import 'zone.js/dist/zone'; 20 | -------------------------------------------------------------------------------- /test/tests-all.js: -------------------------------------------------------------------------------- 1 | import Dexie from 'dexie'; 2 | Dexie.test = true; // Improve code coverage 3 | import "./tests-table.js"; 4 | import "./tests-collection.js"; 5 | import "./tests-whereclause.js"; 6 | import "./tests-transaction.js"; 7 | import "./tests-open.js"; 8 | import "./tests-yield"; 9 | import "./tests-asyncawait.js"; 10 | import "./tests-exception-handling.js"; 11 | import "./tests-upgrading.js"; 12 | import "./tests-misc"; 13 | import "./tests-promise.js"; 14 | import "./tests-extendability.js"; 15 | import "./tests-crud-hooks"; 16 | import "./tests-blobs"; 17 | import "./tests-binarykeys"; 18 | //import "./tests-performance.js"; Not required. Should make other performance tests separately instead. 19 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/tools/build-configs/banner.txt: -------------------------------------------------------------------------------- 1 | /* ========================================================================== 2 | * dexie-syncable.js 3 | * ========================================================================== 4 | * 5 | * Dexie addon for syncing indexedDB with remote endpoints. 6 | * 7 | * By David Fahlander, david.fahlander@gmail.com, 8 | * Nikolas Poniros, https://github.com/nponiros 9 | * 10 | * ========================================================================== 11 | * 12 | * Version {version}, {date} 13 | * 14 | * http://dexie.org 15 | * 16 | * Apache License Version 2.0, January 2004, http://www.apache.org/licenses/ 17 | * 18 | */ 19 | -------------------------------------------------------------------------------- /samples/react-redux/src/components/Todo.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import './Todo.css'; 3 | 4 | const Todo = ({title, id, done, handleToggleTodo, handleDeleteTodo}) =>
  • 5 | handleToggleTodo(id, !done)} 9 | /> 10 | {title} 11 | 12 |
  • ; 13 | 14 | Todo.propTypes = { 15 | title: PropTypes.string.isRequired, 16 | id: PropTypes.number.isRequired, 17 | done: PropTypes.bool, 18 | handleToggleTodo: PropTypes.func.isRequired, 19 | handleDeleteTodo: PropTypes.func.isRequired 20 | }; 21 | 22 | export default Todo; 23 | -------------------------------------------------------------------------------- /samples/react/src/Todo.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { PropTypes } from 'prop-types'; 3 | import './Todo.css'; 4 | 5 | const Todo = ({title, id, done, handleToggleTodo, handleDeleteTodo}) =>
  • 6 | handleToggleTodo(id, !done)} 10 | /> 11 | {title} 12 | 13 |
  • ; 14 | 15 | Todo.propTypes = { 16 | title: PropTypes.string.isRequired, 17 | id: PropTypes.number.isRequired, 18 | done: PropTypes.bool, 19 | handleToggleTodo: PropTypes.func.isRequired, 20 | handleDeleteTodo: PropTypes.func.isRequired 21 | }; 22 | 23 | export default Todo; 24 | -------------------------------------------------------------------------------- /src/helpers/index-spec.ts: -------------------------------------------------------------------------------- 1 | import { IndexSpec } from '../public/types/index-spec'; 2 | 3 | export function createIndexSpec( 4 | name: string, 5 | keyPath?: string | string[], 6 | unique?: boolean, 7 | multi?: boolean, 8 | auto?: boolean, 9 | compound?: boolean, 10 | ): IndexSpec { 11 | return { 12 | name, 13 | keyPath, 14 | unique, 15 | multi, 16 | auto, 17 | compound, 18 | src: (unique ? '&' : '') + (multi ? '*' : '') + (auto ? "++" : "") + nameFromKeyPath(keyPath) 19 | } 20 | } 21 | 22 | export function nameFromKeyPath (keyPath?: string | string[]): string { 23 | return typeof keyPath === 'string' ? 24 | keyPath : 25 | keyPath ? ('[' + [].join.call(keyPath, '+') + ']') : ""; 26 | } 27 | -------------------------------------------------------------------------------- /samples/react-redux/src/components/TodoList.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | 3 | import Todo from './Todo'; 4 | 5 | const TodoList = ({todos, handleToggleTodo, handleDeleteTodo}) =>
      6 | {todos.map((todo) => )} 12 |
    ; 13 | 14 | TodoList.propTypes = { 15 | todos: PropTypes.arrayOf(PropTypes.shape({ 16 | title: PropTypes.string.isRequired, 17 | id: PropTypes.number.isRequired, 18 | done: PropTypes.bool, 19 | })), 20 | handleToggleTodo: PropTypes.func.isRequired, 21 | handleDeleteTodo: PropTypes.func.isRequired 22 | }; 23 | 24 | export default TodoList; 25 | -------------------------------------------------------------------------------- /samples/react/src/TodoList.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { PropTypes } from 'prop-types'; 3 | 4 | import Todo from './Todo'; 5 | 6 | const TodoList = ({todos, handleToggleTodo, handleDeleteTodo}) =>
      7 | {todos.map((todo) => )} 13 |
    ; 14 | 15 | TodoList.propTypes = { 16 | todos: PropTypes.arrayOf(PropTypes.shape({ 17 | title: PropTypes.string.isRequired, 18 | id: PropTypes.number.isRequired, 19 | done: PropTypes.bool, 20 | })), 21 | handleToggleTodo: PropTypes.func.isRequired, 22 | handleDeleteTodo: PropTypes.func.isRequired 23 | }; 24 | 25 | export default TodoList; 26 | -------------------------------------------------------------------------------- /samples/requirejs-with-addons/scripts/db.js: -------------------------------------------------------------------------------- 1 | 2 | define(['dexie', 'dexie-observable', './console'], function (Dexie, DexieObservable, console) { 3 | 4 | // Declare Dexie instance and explicitely apply the addon: 5 | var db = new Dexie("appdb2", { addons: [DexieObservable] }); 6 | 7 | // Define database schema 8 | db.version(1).stores({ 9 | contacts: '++id,first,last' 10 | }); 11 | 12 | 13 | // Populate ground data 14 | db.on('populate', function () { 15 | console.log("Populating data first time"); 16 | // Populate a contact 17 | db.contacts.add({ first: 'Arnold', last: 'Fitzgerald' }); 18 | }); 19 | 20 | // Open database 21 | db.open(); 22 | 23 | return db; 24 | }); 25 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/src/override-open.js: -------------------------------------------------------------------------------- 1 | export default function initOverrideOpen(db, SyncNode, crudMonitor) { 2 | return function overrideOpen(origOpen) { 3 | return function () { 4 | // 5 | // Make sure to subscribe to "creating", "updating" and "deleting" hooks for all observable tables that were created in the stores() method. 6 | // 7 | Object.keys(db._allTables).forEach(tableName => { 8 | let table = db._allTables[tableName]; 9 | if (table.schema.observable) { 10 | crudMonitor(table); 11 | } 12 | if (table.name === "_syncNodes") { 13 | table.mapToClass(SyncNode); 14 | } 15 | }); 16 | return origOpen.apply(this, arguments); 17 | } 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /samples/react-redux/src/containers/App.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | 3 | import App from '../components/App'; 4 | 5 | import { 6 | addTodo, 7 | updateTodo, 8 | deleteTodo, 9 | } from '../actions'; 10 | 11 | function mapStateToProps(state) { 12 | const { todos } = state; 13 | return { 14 | todos, 15 | }; 16 | } 17 | 18 | function mapDispatchToProps(dispatch) { 19 | return { 20 | handleAddTodo(title) { 21 | dispatch(addTodo(title)); 22 | }, 23 | handleDeleteTodo(id) { 24 | dispatch(deleteTodo(id)); 25 | }, 26 | handleUpdateTodo(id, done) { 27 | dispatch(updateTodo(id, done)); 28 | }, 29 | }; 30 | } 31 | 32 | export default connect( 33 | mapStateToProps, 34 | mapDispatchToProps, 35 | )(App); 36 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dexie", 3 | "version": "3.0.0-alpha.7", 4 | "homepage": "http://dexie.org", 5 | "authors": [ 6 | "David Fahlander" 7 | ], 8 | "description": "Minimalistic IndexedDB API with bullet proof transactions", 9 | "main": "dist/dexie.js", 10 | "moduleType": [ 11 | "amd", 12 | "es6", 13 | "node", 14 | "globals" 15 | ], 16 | "keywords": [ 17 | "indexeddb", 18 | "browser", 19 | "database" 20 | ], 21 | "license": "Apache-2.0", 22 | "ignore": [ 23 | "**/.*", 24 | "**/tmp/", 25 | "addons/*/bower.json", 26 | "addons/*/package.json", 27 | "addons/*/test/", 28 | "addons/*/tools/", 29 | "samples/", 30 | "*.njsproj", 31 | "src", 32 | "test", 33 | "tools" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /src/dbcore/get-key-extractor.ts: -------------------------------------------------------------------------------- 1 | import { exceptions } from '../errors'; 2 | import { isArray, getByKeyPath } from '../functions/utils'; 3 | import { Key } from '../public/types/dbcore'; 4 | 5 | export function getKeyExtractor (keyPath: null | string | string[]) : (a: any) => Key { 6 | if (keyPath == null) { 7 | return () => undefined; 8 | } else if (typeof keyPath === 'string') { 9 | return getSinglePathKeyExtractor(keyPath); 10 | } else { 11 | return obj => getByKeyPath(obj, keyPath); 12 | } 13 | } 14 | 15 | export function getSinglePathKeyExtractor(keyPath: string) { 16 | const split = keyPath.split('.'); 17 | if (split.length === 1) { 18 | return obj => obj[keyPath]; 19 | } else { 20 | return obj => getByKeyPath(obj, keyPath); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/dbcore/get-effective-keys.ts: -------------------------------------------------------------------------------- 1 | import { AddRequest, PutRequest, DeleteRequest, Key, DBCoreIndex, DBCoreTable } from '../public/types/dbcore'; 2 | 3 | export function getEffectiveKeys ( 4 | primaryKey: DBCoreIndex, 5 | req: (Pick & {keys?: Key[]}) | Pick) 6 | { 7 | //const {outbound} = primaryKey; 8 | if (req.type === 'delete') return req.keys; 9 | return req.keys || req.values.map(primaryKey.extractKey) 10 | } 11 | 12 | export function getExistingValues (table: DBCoreTable, req: AddRequest | PutRequest | DeleteRequest, effectiveKeys: Key[]) { 13 | return req.type === 'add' ? Promise.resolve(new Array(req.values.length)) : 14 | table.getMany({trans: req.trans, keys: effectiveKeys}); 15 | } 16 | -------------------------------------------------------------------------------- /samples/react-redux/src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | LOAD_TODOS, 3 | ADD_TODO, 4 | UPDATE_TODO, 5 | DELETE_TODO, 6 | } from '../constants'; 7 | 8 | export default function (state, { type, payload }) { 9 | switch (type) { 10 | case LOAD_TODOS: return { todos: payload }; 11 | case ADD_TODO: return { todos: [...state.todos, payload] }; 12 | case UPDATE_TODO: { 13 | const todoToUpdate = state.todos.find((todo) => todo.id === payload.id); 14 | return { todos: [ 15 | ...state.todos.filter((todo) => todo.id !== payload.id), 16 | Object.assign({}, todoToUpdate, { done: payload.done }), 17 | ] }; 18 | } 19 | case DELETE_TODO: return { todos: state.todos.filter((todo) => todo.id !== payload) }; 20 | default: return state; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /samples/requirejs-with-addons/scripts/app.js: -------------------------------------------------------------------------------- 1 | 2 | require.config({ 3 | paths: { 4 | "dexie": "https://unpkg.com/dexie/dist/dexie", 5 | "dexie-observable": "https://unpkg.com/dexie-observable/dist/dexie-observable" 6 | } 7 | }); 8 | 9 | requirejs(['dexie', './console', './db', './ChangeLogger'], function (Dexie, console, db, ChangeLogger) { 10 | 11 | console.log("Hello world!"); 12 | 13 | ChangeLogger(db); 14 | 15 | console.log("Now adding a contact to database"); 16 | db.contacts.add({ first: "Another", last: "Person" }).then(function () { 17 | 18 | console.log("Now deleting that contact from database"); 19 | return db.contacts.where('first').equals("Another").and(function(p) { return p.last == "Person"; }).delete(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/errors/errors.d.ts: -------------------------------------------------------------------------------- 1 | import { 2 | DexieError, 3 | DexieErrorConstructor, 4 | ModifyError, 5 | ModifyErrorConstructor, 6 | BulkError, 7 | BulkErrorConstructor, 8 | ExceptionAliasSet, 9 | ExceptionSet 10 | } from "../public/types/errors"; 11 | 12 | export declare const DexieError: DexieErrorConstructor; 13 | export declare const ModifyError: ModifyErrorConstructor; 14 | export declare const BulkError: BulkErrorConstructor; 15 | export declare const exceptions : ExceptionAliasSet & { 16 | Syntax: ErrorConstructor, 17 | Type: ErrorConstructor, 18 | Range: ErrorConstructor 19 | }; 20 | export declare const errnames : {[P in keyof ExceptionSet]: P}; 21 | export declare const fullNameExceptions : ExceptionSet; 22 | export declare function mapError (domError, message?) : DexieError; 23 | -------------------------------------------------------------------------------- /src/helpers/yield-support.ts: -------------------------------------------------------------------------------- 1 | import { isArray } from '../functions/utils'; 2 | 3 | export function awaitIterator (iterator: Iterator) { 4 | var callNext = result => iterator.next(result), 5 | doThrow = error => iterator.throw(error), 6 | onSuccess = step(callNext), 7 | onError = step(doThrow); 8 | 9 | function step(getNext: (any)=>any) { 10 | return (val?) => { 11 | var next = getNext(val), 12 | value = next.value; 13 | 14 | return next.done ? value : 15 | (!value || typeof value.then !== 'function' ? 16 | isArray(value) ? Promise.all(value).then(onSuccess, onError) : onSuccess(value) : 17 | value.then(onSuccess, onError)); 18 | }; 19 | } 20 | 21 | return step(callNext)(); 22 | } 23 | -------------------------------------------------------------------------------- /test/run-unit-tests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Dexie Unit tests 6 | 7 | 8 | 9 |
    10 |
    11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/src/statuses.js: -------------------------------------------------------------------------------- 1 | export const Statuses = { 2 | ERROR: -1, // An irreparable error occurred and the sync provider is dead. 3 | OFFLINE: 0, // The sync provider hasn't yet become online, or it has been disconnected. 4 | CONNECTING: 1, // Trying to connect to server 5 | ONLINE: 2, // Connected to server and currently in sync with server 6 | SYNCING: 3, // Syncing with server. For poll pattern, this is every poll call. For react pattern, this is when local changes are being sent to server. 7 | ERROR_WILL_RETRY: 4 // An error occurred such as net down but the sync provider will retry to connect. 8 | }; 9 | 10 | export const StatusTexts = { 11 | "-1": "ERROR", 12 | "0": "OFFLINE", 13 | "1": "CONNECTING", 14 | "2": "ONLINE", 15 | "3": "SYNCING", 16 | "4": "ERROR_WILL_RETRY" 17 | }; 18 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/src/hooks/crud-monitor.js: -------------------------------------------------------------------------------- 1 | import initCreatingHook from './creating'; 2 | import initUpdatingHook from './updating'; 3 | import initDeletingHook from './deleting'; 4 | 5 | export default function initCrudMonitor(db) { 6 | // 7 | // The Creating/Updating/Deleting hook will make sure any change is stored to the changes table 8 | // 9 | return function crudMonitor(table) { 10 | /// 11 | if (table.hook._observing) return; 12 | table.hook._observing = true; 13 | 14 | const tableName = table.name; 15 | table.hook('creating').subscribe(initCreatingHook(db, table)); 16 | 17 | table.hook('updating').subscribe(initUpdatingHook(db, tableName)); 18 | 19 | table.hook('deleting').subscribe(initDeletingHook(db, tableName)); 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/src/save-to-uncommitted-changes.js: -------------------------------------------------------------------------------- 1 | export default function initSaveToUncommittedChanges(db, node) { 2 | return function saveToUncommittedChanges(changes, remoteRevision) { 3 | return db.transaction('rw!', db._uncommittedChanges, () => { 4 | return db._uncommittedChanges.bulkAdd(changes.map(change => { 5 | let changeWithNodeId = { 6 | node: node.id, 7 | type: change.type, 8 | table: change.table, 9 | key: change.key 10 | }; 11 | if (change.obj) changeWithNodeId.obj = change.obj; 12 | if (change.mods) changeWithNodeId.mods = change.mods; 13 | return changeWithNodeId; 14 | })); 15 | }).then(() => { 16 | node.appliedRemoteRevision = remoteRevision; 17 | return node.save(); 18 | }); 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /samples/angular2/src/app/core/core.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, Optional, SkipSelf } from '@angular/core'; 2 | 3 | import {DexieService} from './dexie.service'; 4 | 5 | // See: https://angular.io/docs/ts/latest/cookbook/ngmodule-faq.html#!#q-is-it-loaded 6 | // and: https://angular.io/docs/ts/latest/guide/ngmodule.html#!#core-module for an explanation on what the CoreModule is 7 | // and what its constructor does 8 | 9 | @NgModule({ 10 | declarations: [], 11 | imports: [], 12 | providers: [ 13 | DexieService, 14 | ], 15 | bootstrap: [] 16 | }) 17 | export class CoreModule { 18 | constructor(@Optional() @SkipSelf() parentModule: CoreModule) { 19 | if (parentModule) { 20 | throw new Error( 21 | 'CoreModule is already loaded. Import it in the AppModule only'); 22 | } 23 | } 24 | } 25 | 26 | 27 | -------------------------------------------------------------------------------- /samples/browserify/scripts/contact.js: -------------------------------------------------------------------------------- 1 | var db = require("./db"); 2 | 3 | function Contact(id, first, last) { 4 | this.id = id; 5 | this.first = first; 6 | this.last = last; 7 | } 8 | 9 | Contact.prototype.resolve = function () { 10 | var contact = this; 11 | 12 | return db.emails.where('contactId').equals(contact.id).toArray(function (emails) { 13 | 14 | return db.phones.where('contactId').equals(contact.id).toArray(function (phones) { 15 | 16 | return { 17 | id: contact.id, 18 | first: contact.first, 19 | last: contact.last, 20 | emails: emails, 21 | phones: phones 22 | }; 23 | }); 24 | }); 25 | }; 26 | 27 | db.contacts.mapToClass(Contact); 28 | 29 | module.exports = Contact; 30 | -------------------------------------------------------------------------------- /samples/angular2/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { FormsModule } from '@angular/forms'; 4 | 5 | import { CoreModule } from './core/core.module'; 6 | 7 | import { AppComponent } from './app.component'; 8 | import { TodoListComponent } from './app-todo-list.component'; 9 | import { AddTodoComponent } from './app-add-todo.component'; 10 | 11 | import { TodosService } from './todos.service'; 12 | 13 | @NgModule({ 14 | declarations: [ 15 | AppComponent, 16 | TodoListComponent, 17 | AddTodoComponent, 18 | ], 19 | imports: [ 20 | BrowserModule, 21 | FormsModule, 22 | CoreModule, 23 | ], 24 | providers: [ 25 | TodosService, 26 | ], 27 | bootstrap: [AppComponent] 28 | }) 29 | export class AppModule { } 30 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/tools/build-configs/banner.txt: -------------------------------------------------------------------------------- 1 | /* ========================================================================== 2 | * dexie-observable.js 3 | * ========================================================================== 4 | * 5 | * Dexie addon for observing database changes not just on local db instance 6 | * but also on other instances, tabs and windows. 7 | * 8 | * Comprises a base framework for dexie-syncable.js 9 | * 10 | * By David Fahlander, david.fahlander@gmail.com, 11 | * Nikolas Poniros, https://github.com/nponiros 12 | * 13 | * ========================================================================== 14 | * 15 | * Version {version}, {date} 16 | * 17 | * http://dexie.org 18 | * 19 | * Apache License Version 2.0, January 2004, http://www.apache.org/licenses/ 20 | * 21 | */ 22 | -------------------------------------------------------------------------------- /samples/typescript-simple/app.ts: -------------------------------------------------------------------------------- 1 | import Dexie from 'dexie'; 2 | 3 | interface IFriend { 4 | id?: number; 5 | name?: string; 6 | age?: number; 7 | } 8 | 9 | // 10 | // Declare Database 11 | // 12 | class FriendDatabase extends Dexie { 13 | friends: Dexie.Table; 14 | 15 | constructor() { 16 | super("FriendsDatabase"); 17 | this.version(1).stores({ 18 | friends: "++id,name,age" 19 | }); 20 | } 21 | } 22 | 23 | var db = new FriendDatabase(); 24 | // 25 | // Manipulate and Query Database 26 | // 27 | db.friends.add({name: "Josephine", age: 21}).then(()=>{ 28 | return db.friends.where("age").below(25).toArray(); 29 | }).then(youngFriends => { 30 | alert ("My young friends: " + JSON.stringify(youngFriends)); 31 | }).catch(e => { 32 | alert("error: " + e.stack || e); 33 | }); 34 | -------------------------------------------------------------------------------- /samples/angular2/src/app/todos.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import Dexie from 'dexie'; 3 | 4 | import { DexieService } from './core/dexie.service'; 5 | 6 | export interface Todo { 7 | title: string; 8 | done: boolean; 9 | } 10 | 11 | export interface TodoWithID extends Todo { 12 | id: number; 13 | } 14 | 15 | @Injectable() 16 | export class TodosService { 17 | table: Dexie.Table; 18 | 19 | constructor(private dexieService: DexieService) { 20 | this.table = this.dexieService.table('todos'); 21 | } 22 | 23 | getAll() { 24 | return this.table.toArray(); 25 | } 26 | 27 | add(data) { 28 | return this.table.add(data); 29 | } 30 | 31 | update(id, data) { 32 | return this.table.update(id, data); 33 | } 34 | 35 | remove(id) { 36 | return this.table.delete(id); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /samples/browserify/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dexie-sample-browserify", 3 | "version": "1.0.0", 4 | "description": "Sample on how to use Dexie.js with browserify", 5 | "main": "scripts/main.js", 6 | "scripts": { 7 | "build": "browserify scripts/main.js -o bundle.js", 8 | "test": "http-server . -a localhost -p 8081" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/dfahlander/Dexie.js.git" 13 | }, 14 | "keywords": [ 15 | "dexie", 16 | "samples" 17 | ], 18 | "author": "David Fahlander", 19 | "license": "Apache-2.0", 20 | "bugs": { 21 | "url": "https://github.com/dfahlander/Dexie.js/issues" 22 | }, 23 | "homepage": "https://github.com/dfahlander/Dexie.js#readme", 24 | "dependencies": { 25 | "dexie": "1.x" 26 | }, 27 | "devDependencies": { 28 | "browserify": "^13.0.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /samples/react-redux/src/components/AddTodo.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react'; 2 | 3 | class AddTodo extends Component { 4 | constructor() { 5 | super(); 6 | this.state = {value: ''}; 7 | this.addTodo = this.addTodo.bind(this); 8 | this.handleChange = this.handleChange.bind(this); 9 | } 10 | 11 | addTodo() { 12 | this.props.handleAddTodo(this.state.value); 13 | } 14 | 15 | handleChange(newValue) { 16 | this.setState({value: newValue}); 17 | } 18 | 19 | render() { 20 | return (
    21 | this.handleChange(e.target.value)} /> 22 | 23 |
    ); 24 | } 25 | } 26 | 27 | AddTodo.propTypes = { 28 | handleAddTodo: PropTypes.func.isRequired, 29 | }; 30 | 31 | export default AddTodo; 32 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/src/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint:recommended", 3 | "parserOptions": { 4 | "ecmaVersion": 6, 5 | "sourceType": "module", 6 | "ecmaFeatures": { 7 | } 8 | }, 9 | "rules": { 10 | "no-undef": ["error"], 11 | "no-unused-vars": 1, 12 | "no-console": 0, 13 | "no-empty": 0 14 | }, 15 | "globals": { 16 | "indexedDB": false, 17 | "IDBKeyRange": false, 18 | "setTimeout": false, 19 | "clearTimeout": false, 20 | "Symbol": false, 21 | "setImmediate": false, 22 | "console": false, 23 | "self": false, 24 | "window": false, 25 | "global": false, 26 | "navigator": false, 27 | "location": false, 28 | "chrome": false, 29 | "document": false, 30 | "MutationObserver": false, 31 | "CustomEvent": false, 32 | "dispatchEvent": false, 33 | "localStorage": false 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/src/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint:recommended", 3 | "parserOptions": { 4 | "ecmaVersion": 6, 5 | "sourceType": "module", 6 | "ecmaFeatures": { 7 | } 8 | }, 9 | "rules": { 10 | "no-undef": ["error"], 11 | "no-unused-vars": 1, 12 | "no-console": 0, 13 | "no-empty": 0 14 | }, 15 | "globals": { 16 | "indexedDB": false, 17 | "IDBKeyRange": false, 18 | "setTimeout": false, 19 | "clearTimeout": false, 20 | "Symbol": false, 21 | "setImmediate": false, 22 | "console": false, 23 | "self": false, 24 | "window": false, 25 | "global": false, 26 | "navigator": false, 27 | "location": false, 28 | "chrome": false, 29 | "document": false, 30 | "MutationObserver": false, 31 | "CustomEvent": false, 32 | "dispatchEvent": false, 33 | "localStorage": false 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /samples/react/src/AddTodo.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { PropTypes } from 'prop-types'; 3 | 4 | class AddTodo extends Component { 5 | constructor() { 6 | super(); 7 | this.state = {value: ''}; 8 | this.addTodo = this.addTodo.bind(this); 9 | this.handleChange = this.handleChange.bind(this); 10 | } 11 | 12 | addTodo() { 13 | this.props.handleAddTodo(this.state.value); 14 | } 15 | 16 | handleChange(newValue) { 17 | this.setState({value: newValue}); 18 | } 19 | 20 | render() { 21 | return (
    22 | this.handleChange(e.target.value)} /> 23 | 24 |
    ); 25 | } 26 | } 27 | 28 | AddTodo.propTypes = { 29 | handleAddTodo: PropTypes.func.isRequired, 30 | }; 31 | 32 | export default AddTodo; 33 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/tools/build-configs/rollup.config.js: -------------------------------------------------------------------------------- 1 | import sourcemaps from 'rollup-plugin-sourcemaps'; 2 | import {readFileSync} from 'fs'; 3 | import path from 'path'; 4 | 5 | const version = require(path.resolve(__dirname, '../../package.json')).version; 6 | 7 | export default { 8 | entry: 'tools/tmp/es5/addons/Dexie.Syncable/src/Dexie.Syncable.js', 9 | targets: [{ 10 | dest: 'dist/dexie-syncable.js', 11 | format: 'umd', 12 | },{ 13 | dest: 'dist/dexie-syncable.es.js', 14 | format: 'es' 15 | }], 16 | sourceMap: true, 17 | banner: readFileSync(path.resolve(__dirname, 'banner.txt'))+"" 18 | .replace(/{version}/g, version) 19 | .replace(/{date}/g, new Date().toDateString()), 20 | moduleName: 'Dexie.Syncable', 21 | globals: {dexie: "Dexie", "dexie-observable": "Dexie.Observable"}, 22 | external: ['dexie', 'dexie-observable'], 23 | plugins: [ sourcemaps() ] 24 | }; 25 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/src/utils.js: -------------------------------------------------------------------------------- 1 | export function nop() {} 2 | 3 | export function promisableChain(f1, f2) { 4 | if (f1 === nop) return f2; 5 | return function() { 6 | var res = f1.apply(this, arguments); 7 | if (res && typeof res.then === 'function') { 8 | var thiz = this, args = arguments; 9 | return res.then(function() { 10 | return f2.apply(thiz, args); 11 | }); 12 | } 13 | return f2.apply(this, arguments); 14 | }; 15 | } 16 | 17 | export function createUUID() { 18 | // Decent solution from http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript 19 | var d = Date.now(); 20 | var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { 21 | var r = (d + Math.random() * 16) % 16 | 0; 22 | d = Math.floor(d / 16); 23 | return (c === 'x' ? r : (r & 0x7 | 0x8)).toString(16); 24 | }); 25 | return uuid; 26 | } 27 | -------------------------------------------------------------------------------- /samples/angular2/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | /*global jasmine */ 5 | var SpecReporter = require('jasmine-spec-reporter'); 6 | 7 | exports.config = { 8 | allScriptsTimeout: 11000, 9 | specs: [ 10 | './e2e/**/*.e2e-spec.ts' 11 | ], 12 | capabilities: { 13 | 'browserName': 'chrome' 14 | }, 15 | directConnect: true, 16 | baseUrl: 'http://localhost:4200/', 17 | framework: 'jasmine', 18 | jasmineNodeOpts: { 19 | showColors: true, 20 | defaultTimeoutInterval: 30000, 21 | print: function() {} 22 | }, 23 | useAllAngular2AppRoots: true, 24 | beforeLaunch: function() { 25 | require('ts-node').register({ 26 | project: 'e2e' 27 | }); 28 | }, 29 | onPrepare: function() { 30 | jasmine.getEnv().addReporter(new SpecReporter()); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /samples/requirejs/scripts/Contact.js: -------------------------------------------------------------------------------- 1 | 2 | define(['./db'], function (db) { 3 | 4 | function Contact(id, first, last) { 5 | this.id = id; 6 | this.first = first; 7 | this.last = last; 8 | } 9 | 10 | Contact.prototype.resolve = function () { 11 | var contact = this; 12 | 13 | return db.emails.where('contactId').equals(contact.id).toArray(function (emails) { 14 | 15 | return db.phones.where('contactId').equals(contact.id).toArray(function (phones) { 16 | 17 | return { 18 | id: contact.id, 19 | first: contact.first, 20 | last: contact.last, 21 | emails: emails, 22 | phones: phones 23 | }; 24 | }); 25 | }); 26 | }; 27 | 28 | db.contacts.mapToClass(Contact); 29 | 30 | return Contact; 31 | }); 32 | -------------------------------------------------------------------------------- /samples/browserify/scripts/main.js: -------------------------------------------------------------------------------- 1 | var Dexie = require("dexie"), 2 | console = require("./console"), 3 | db = require("./db"), 4 | Contact = require("./contact"); 5 | 6 | db.transaction('r', db.contacts, db.phones, db.emails, function () { 7 | 8 | // Query all contacts 9 | db.contacts.toArray(function (contacts) { 10 | 11 | // Resolve all foreign keys 12 | return Dexie.Promise.all(contacts.map(function (contact) { 13 | 14 | // Use Contact.prototype.resolve() helper method from contact.js 15 | return contact.resolve(); 16 | })); 17 | 18 | }).then(function (resolvedContacts) { 19 | 20 | // Print result 21 | console.log("Database contains the following contacts:"); 22 | console.log(JSON.stringify(resolvedContacts, null, 4)); 23 | }); 24 | 25 | }).then(function () { 26 | 27 | console.log("Transaction done"); 28 | 29 | }); 30 | -------------------------------------------------------------------------------- /samples/typescript-simple/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dexie-sample-typescript-simple", 3 | "version": "1.0.0", 4 | "description": "Simples possible typescript sample with requirejs to minimize build steps", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node node_modules/http-server/bin/http-server . -a localhost -p 8081", 9 | "build": "node node_modules/typescript/bin/tsc" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/dfahlander/Dexie.js.git" 14 | }, 15 | "keywords": [ 16 | "browser", 17 | "dexie", 18 | "sample" 19 | ], 20 | "author": "David Fahlander", 21 | "license": "Apache-2.0", 22 | "devDependencies": { 23 | "http-server": "^0.9.0", 24 | "requirejs": "^2.1.22", 25 | "typescript": "^2.1.4" 26 | }, 27 | "dependencies": { 28 | "dexie": "^2.0.0-beta.6" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/test/unit/unit-tests-all.js: -------------------------------------------------------------------------------- 1 | import './get-local-changes-for-node/tests-get-base-revision-and-max-client-revision.js'; 2 | import './get-local-changes-for-node/tests-get-changes-since-revision.js'; 3 | import './get-local-changes-for-node/tests-get-local-changes-for-node.js'; 4 | import './tests-apply-changes.js'; 5 | import './tests-bulk-update.js'; 6 | import './tests-changing-options.js'; 7 | import './tests-combine-create-and-update.js'; 8 | import './tests-combine-update-and-update.js'; 9 | import './tests-finally-commit-all-changes.js'; 10 | import './tests-get-or-create-sync-node.js'; 11 | import './tests-merge-change.js'; 12 | import './tests-PersistedContext.js'; 13 | import './tests-register-sync-protocol.js'; 14 | import './tests-save-to-uncommitted-changes.js'; 15 | import './tests-syncable.js'; 16 | import './tests-syncable-partials.js'; 17 | import './tests-syncprovider.js'; 18 | import './tests-WebSocketSyncServer.js'; 19 | -------------------------------------------------------------------------------- /samples/react-redux/src/components/App.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import './App.css'; 3 | 4 | import AddTodo from './AddTodo'; 5 | import TodoList from './TodoList'; 6 | 7 | const App = ({todos, handleUpdateTodo, handleDeleteTodo, handleAddTodo}) =>
    8 |
    9 |

    React + Redux + Dexie Todo Example

    10 |
    11 | 12 | 17 |
    ; 18 | 19 | App.propTypes = { 20 | todos: PropTypes.arrayOf(PropTypes.shape({ 21 | title: PropTypes.string.isRequired, 22 | id: PropTypes.number.isRequired, 23 | done: PropTypes.bool, 24 | })), 25 | handleUpdateTodo: PropTypes.func.isRequired, 26 | handleDeleteTodo: PropTypes.func.isRequired, 27 | handleAddTodo: PropTypes.func.isRequired, 28 | }; 29 | 30 | export default App; 31 | -------------------------------------------------------------------------------- /samples/react/README.md: -------------------------------------------------------------------------------- 1 | # React + Dexie Todo Example 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app). 4 | 5 | ## Install dependencies 6 | 7 | Before you can run the app in your browser, you will have to install its dependencies with: 8 | 9 | ``` 10 | npm install 11 | ``` 12 | 13 | ## Development server 14 | 15 | Run `npm start` for a dev server. Navigate to `http://localhost:3000`. The app will automatically reload if you change any of the source files. You will also see any lint errors in the console. 16 | 17 | ## Build 18 | 19 | Run `npm run build` to build the project. The build artifacts will be stored in the `build/` directory. 20 | 21 | ## Further help 22 | 23 | * [Create React App User Guide](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md). 24 | * To get more help on Dexie check out the [Dexie Wiki](https://github.com/dfahlander/Dexie.js/wiki) 25 | -------------------------------------------------------------------------------- /test/worker.js: -------------------------------------------------------------------------------- 1 | 2 | onmessage = function (e) { 3 | var imports = e.data.imports || []; 4 | var code = e.data.code; 5 | if (imports.length > 0) 6 | importScripts.apply(self, imports); 7 | 8 | var pCodeBegin = code.indexOf('{'), 9 | pCodeEnd = code.lastIndexOf('}'); 10 | if (pCodeBegin === -1 || pCodeEnd === -1) { 11 | postMessage(["ok", false, "Worker.js error: Provided code must be (a Function).toString()"]); 12 | postMessage(["done"]); 13 | return; 14 | } 15 | try { 16 | code = code.substring(pCodeBegin + 1, pCodeEnd); 17 | new Function("ok", "done", code)(function ok(b, msg) { 18 | postMessage(["ok", b, msg]); 19 | }, function() { 20 | postMessage(["done"]); 21 | }); 22 | } catch (ex) { 23 | postMessage(["ok", false, "Worker error: " + ex.toString() + (ex.stack ? "\n" + ex.stack : "")]); 24 | postMessage(["done"]); 25 | return; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/globals/constants.ts: -------------------------------------------------------------------------------- 1 | import { Dexie } from "../classes/dexie"; 2 | 3 | export const DEXIE_VERSION = '{version}'; // Replaced by build-script. 4 | export const maxString = String.fromCharCode(65535); 5 | export const minKey = -Infinity; // minKey can be constant. maxKey must be a prop of Dexie (_maxKey) 6 | export const INVALID_KEY_ARGUMENT = 7 | "Invalid key provided. Keys must be of type string, number, Date or Array."; 8 | export const STRING_EXPECTED = "String expected."; 9 | export const connections: Dexie[] = []; 10 | export const isIEOrEdge = 11 | typeof navigator !== 'undefined' && /(MSIE|Trident|Edge)/.test(navigator.userAgent); 12 | export const hasIEDeleteObjectStoreBug = isIEOrEdge; 13 | export const hangsOnDeleteLargeKeyRange = isIEOrEdge; 14 | export const dexieStackFrameFilter = frame => !/(dexie\.js|dexie\.min\.js)/.test(frame); 15 | export const DBNAMES_DB = '__dbnames'; 16 | export const READONLY = 'readonly'; 17 | export const READWRITE = 'readwrite'; 18 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/test/integration/test-observable-dexie-tests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Dexie Unit tests with Dexie.Observable applied 6 | 7 | 8 | 9 | 10 |
    11 |
    12 | 13 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/test/unit/run-unit-tests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Dexie.Syncable Unit tests 6 | 7 | 8 | 9 |
    10 |
    11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /samples/browserify/scripts/db.js: -------------------------------------------------------------------------------- 1 | var Dexie = require("dexie"); 2 | 3 | // Declare Dexie instance 4 | var db = new Dexie("maindb"); 5 | 6 | // Define database schema 7 | db.version(1).stores({ 8 | contacts: '++id,first,last', 9 | emails: '++id,contactId,type,email', 10 | phones: '++id,contactId,type,phone', 11 | }); 12 | 13 | // Populate ground data 14 | db.on('populate', function () { 15 | 16 | // Populate a contact 17 | db.contacts.add({ first: 'Arnold', last: 'Fitzgerald' }).then(function (id) { 18 | 19 | // Populate some emails and phone numbers for the contact 20 | db.emails.add({ contactId: id, type: 'home', email: 'arnold@email.com' }); 21 | db.emails.add({ contactId: id, type: 'work', email: 'arnold@abc.com' }); 22 | db.phones.add({ contactId: id, type: 'home', phone: '12345678' }); 23 | db.phones.add({ contactId: id, type: 'work', phone: '987654321' }); 24 | }); 25 | }); 26 | 27 | // Open database 28 | db.open(); 29 | 30 | module.exports = db; 31 | -------------------------------------------------------------------------------- /src/public/types/db-events.d.ts: -------------------------------------------------------------------------------- 1 | import { DexieEventSet } from "./dexie-event-set"; 2 | import { DexieEvent } from "./dexie-event"; 3 | 4 | export interface DexieOnReadyEvent { 5 | subscribe(fn: () => any, bSticky: boolean): void; 6 | unsubscribe(fn: () => any): void; 7 | fire(): any; 8 | } 9 | 10 | export interface DexieVersionChangeEvent { 11 | subscribe(fn: (event: IDBVersionChangeEvent) => any): void; 12 | unsubscribe(fn: (event: IDBVersionChangeEvent) => any): void; 13 | fire(event: IDBVersionChangeEvent): any; 14 | } 15 | 16 | export interface DbEvents extends DexieEventSet { 17 | (eventName: 'ready', subscriber: () => any, bSticky?: boolean): void; 18 | (eventName: 'populate', subscriber: () => any): void; 19 | (eventName: 'blocked', subscriber: (event: IDBVersionChangeEvent) => any): void; 20 | (eventName: 'versionchange', subscriber: (event: IDBVersionChangeEvent) => any): void; 21 | ready: DexieOnReadyEvent; 22 | populate: DexieEvent; 23 | blocked: DexieEvent; 24 | versionchange: DexieVersionChangeEvent; 25 | } 26 | -------------------------------------------------------------------------------- /samples/angular2/src/app/app-todo-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, Output, EventEmitter } from '@angular/core'; 2 | 3 | import { TodoWithID } from './todos.service'; 4 | 5 | @Component({ 6 | selector: 'app-todo-list', 7 | styles: [`ul li { list-style: none; }`], 8 | template: `
      9 |
    • 10 | 11 | {{todo.title}} 12 | 13 |
    • 14 |
    ` 15 | }) 16 | export class TodoListComponent { 17 | @Input() todos: Array; 18 | @Output() toggleTodo = new EventEmitter(); 19 | @Output() deleteTodo = new EventEmitter(); 20 | 21 | onTodoToggle(event, id, newValue) { 22 | event.preventDefault(); 23 | event.stopPropagation(); 24 | this.toggleTodo.emit({ 25 | id, 26 | done: newValue, 27 | }); 28 | } 29 | 30 | onDelete(id) { 31 | this.deleteTodo.emit(id); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /samples/requirejs/scripts/app.js: -------------------------------------------------------------------------------- 1 | 2 | require.config({ 3 | paths: { 4 | "dexie": "https://unpkg.com/dexie/dist/dexie" 5 | } 6 | }); 7 | 8 | requirejs(['dexie', './console', './db', './Contact'], function (Dexie, console, db, Contact) { 9 | 10 | db.transaction('r', db.contacts, db.phones, db.emails, function () { 11 | 12 | // Query all contacts 13 | db.contacts.toArray(function (contacts) { 14 | 15 | // Resolve all foreign keys 16 | return Dexie.Promise.all(contacts.map(function (contact) { 17 | 18 | // Use Contact.resolve() helper method from Contact.js 19 | return contact.resolve(); 20 | })); 21 | 22 | }).then(function (resolvedContacts) { 23 | 24 | // Print result 25 | console.log("Database contains the following contacts:"); 26 | console.log(JSON.stringify(resolvedContacts, null, 4)); 27 | }); 28 | 29 | }).then(function () { 30 | 31 | console.log("Transaction done"); 32 | 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /tools/build-configs/rollup.tests.config.js: -------------------------------------------------------------------------------- 1 | import sourcemaps from 'rollup-plugin-sourcemaps'; 2 | import commonjs from 'rollup-plugin-commonjs'; 3 | import nodeResolve from 'rollup-plugin-node-resolve'; 4 | import path from 'path'; 5 | 6 | const ERRORS_TO_IGNORE = [ 7 | "THIS_IS_UNDEFINED" 8 | ]; 9 | 10 | export default { 11 | input: path.resolve(__dirname, '../tmp/test/tests-all.js'), 12 | output: { 13 | file: path.resolve(__dirname, '../../test/bundle.js'), 14 | format: 'umd', 15 | sourcemap: true, 16 | name: 'dexieTests', 17 | globals: {dexie: "Dexie", QUnit: "QUnit"}, 18 | }, 19 | external: ['dexie', 'QUnit'], 20 | plugins: [ 21 | sourcemaps(), 22 | nodeResolve({browser: true}), 23 | commonjs() 24 | ], 25 | onwarn ({loc, frame, code, message}) { 26 | if (ERRORS_TO_IGNORE.includes(code)) return; 27 | if ( loc ) { 28 | console.warn( `${loc.file} (${loc.line}:${loc.column}) ${message}` ); 29 | if ( frame ) console.warn( frame ); 30 | } else { 31 | console.warn(`${code} ${message}`); 32 | } 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/tools/build-configs/rollup.tests.config.js: -------------------------------------------------------------------------------- 1 | import sourcemaps from 'rollup-plugin-sourcemaps'; 2 | import commonjs from 'rollup-plugin-commonjs'; 3 | import nodeResolve from 'rollup-plugin-node-resolve'; 4 | 5 | const ERRORS_TO_IGNORE = [ 6 | "THIS_IS_UNDEFINED" 7 | ]; 8 | 9 | export default { 10 | entry: 'tools/tmp/es5/addons/Dexie.Syncable/test/unit/unit-tests-all.js', 11 | dest: 'test/unit/bundle.js', 12 | format: 'umd', 13 | sourceMap: true, 14 | moduleName: 'dexieSyncableTests', 15 | globals: {dexie: "Dexie", "dexie-observable": "Dexie.Observable", QUnit: "QUnit"}, 16 | external: ['dexie', 'dexie-observable', 'QUnit'], 17 | plugins: [ 18 | sourcemaps(), 19 | nodeResolve({browser: true}), 20 | commonjs() 21 | ], 22 | onwarn ({loc, frame, code, message}) { 23 | if (ERRORS_TO_IGNORE.includes(code)) return; 24 | if ( loc ) { 25 | console.warn( `${loc.file} (${loc.line}:${loc.column}) ${message}` ); 26 | if ( frame ) console.warn( frame ); 27 | } else { 28 | console.warn(`${code} ${message}`); 29 | } 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /src/classes/version/version-constructor.ts: -------------------------------------------------------------------------------- 1 | import { Dexie } from '../dexie'; 2 | import { makeClassConstructor } from '../../functions/make-class-constructor'; 3 | import { Version } from './version'; 4 | 5 | export interface VersionConstructor { 6 | new(versionNumber: number): Version; 7 | prototype: Version; 8 | } 9 | 10 | /** Generates a Version constructor bound to given Dexie instance. 11 | * 12 | * The purpose of having dynamically created constructors, is to allow 13 | * addons to extend classes for a certain Dexie instance without affecting 14 | * other db instances. 15 | */ 16 | export function createVersionConstructor(db: Dexie) { 17 | return makeClassConstructor( 18 | Version.prototype, 19 | 20 | function Version(this: Version, versionNumber: number) { 21 | this.db = db; 22 | this._cfg = { 23 | version: versionNumber, 24 | storesSource: null, 25 | dbschema: {}, 26 | tables: {}, 27 | contentUpgrade: null 28 | }; 29 | this.stores({}); // Derive earlier schemas by default. 30 | }); 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/dbcore/key-resolver-middleware.ts: -------------------------------------------------------------------------------- 1 | import { getEffectiveKeys } from '../dbcore/get-effective-keys'; 2 | import { DBCore } from '../public/types/dbcore'; 3 | 4 | /** Resolve effective keys into req.keys on all mutate requests. 5 | * This will make DBCore able to set results array in the response 6 | * by cloning the effective keys and add auto-incremented results where 7 | * applicable. 8 | * 9 | * This middleware should run on a high prio before other middlewares. 10 | * Also hooks middleware can take advantage of this info. 11 | */ 12 | export function createKeyResolverMiddleware(down: DBCore) : DBCore { 13 | return { 14 | ...down, 15 | table (tableName) { 16 | const table = down.table(tableName); 17 | const {primaryKey} = table.schema; 18 | return { 19 | ...table, 20 | mutate(req) { 21 | if (req.type === 'add' || req.type === 'put') { 22 | const keys = getEffectiveKeys(primaryKey, req); 23 | req = {...req, keys}; 24 | return table.mutate(req); 25 | } 26 | } 27 | }; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /addons/Dexie.Observable/tools/build-configs/rollup.config.js: -------------------------------------------------------------------------------- 1 | import sourcemaps from 'rollup-plugin-sourcemaps'; 2 | import {readFileSync} from 'fs'; 3 | import path from 'path'; 4 | 5 | const version = require(path.resolve(__dirname, '../../package.json')).version; 6 | 7 | export default { 8 | input: 'tools/tmp/es5/src/Dexie.Observable.js', 9 | output: [{ 10 | file: 'dist/dexie-observable.js', 11 | format: 'umd', 12 | banner: readFileSync(path.resolve(__dirname, 'banner.txt'))+"" 13 | .replace(/{version}/g, version) 14 | .replace(/{date}/g, new Date().toDateString()), 15 | globals: {dexie: "Dexie"}, 16 | name: 'Dexie.Observable', 17 | sourcemap: true 18 | },{ 19 | file: 'dist/dexie-observable.es.js', 20 | format: 'es', 21 | banner: readFileSync(path.resolve(__dirname, 'banner.txt'))+"" 22 | .replace(/{version}/g, version) 23 | .replace(/{date}/g, new Date().toDateString()), 24 | globals: {dexie: "Dexie"}, 25 | name: 'Dexie.Observable', 26 | sourcemap: true 27 | }], 28 | external: ['dexie'], 29 | plugins: [ sourcemaps() ] 30 | }; 31 | -------------------------------------------------------------------------------- /test/integrations/dexie-relationships/rollup.config.js: -------------------------------------------------------------------------------- 1 | import sourcemaps from 'rollup-plugin-sourcemaps'; 2 | import commonjs from 'rollup-plugin-commonjs'; 3 | import nodeResolve from 'rollup-plugin-node-resolve'; 4 | 5 | const ERRORS_TO_IGNORE = [ 6 | "THIS_IS_UNDEFINED" 7 | ]; 8 | 9 | export default { 10 | entry: 'tmp/es5/integrations/dexie-relationships/index.js', 11 | dest: 'dexie-relationships/test-bundle.js', 12 | format: 'umd', 13 | sourceMap: true, 14 | moduleName: 'dexieRelationshipsIntegrationTests', 15 | globals: {dexie: "Dexie", "dexie-relationships": "dexieRelationships", QUnit: "QUnit"}, 16 | external: ['dexie', 'dexie-relationships', 'QUnit'], 17 | plugins: [ 18 | sourcemaps(), 19 | nodeResolve({browser: true}), 20 | commonjs() 21 | ], 22 | onwarn ({loc, frame, code, message}) { 23 | if (ERRORS_TO_IGNORE.includes(code)) return; 24 | if ( loc ) { 25 | console.warn( `${loc.file} (${loc.line}:${loc.column}) ${message}` ); 26 | if ( frame ) console.warn( frame ); 27 | } else { 28 | console.warn(`${code} ${message}`); 29 | } 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /test/typings-test/test-misc.ts: -------------------------------------------------------------------------------- 1 | import Dexie from '../../dist/dexie'; 2 | 3 | class MyDb extends Dexie { 4 | friends: Dexie.Table<{name, id}, number>; 5 | constructor() { 6 | super("MyDb"); 7 | this.version(1).stores({ 8 | friends: '++id,age' 9 | }); 10 | 11 | } 12 | } 13 | /* 14 | interface Addon { 15 | (db: DB):void; 16 | XTable?: XTable; 17 | XCollection?: XCollection; 18 | } 19 | 20 | type TblSchema = {value: T, key: K}; 21 | type Schema = {}; 22 | interface Friend {name: string, id: number}; 23 | var x: Schema<{friends: Friend, key: number}}>; 24 | 25 | type Addons = {0?: A1, 1?:A2}; 26 | type DBX = {schema: TSchema, addons} 27 | 28 | class DB { 29 | constructor(options: {addons: {0: Ext1, 1:Ext2}}) { 30 | 31 | } 32 | 33 | get friends() { 34 | return this.table('friends'); 35 | } 36 | } 37 | */ -------------------------------------------------------------------------------- /samples/requirejs/scripts/db.js: -------------------------------------------------------------------------------- 1 | 2 | define(['dexie'], function (Dexie) { 3 | 4 | // Declare Dexie instance 5 | var db = new Dexie("appdb"); 6 | 7 | // Define database schema 8 | db.version(1).stores({ 9 | contacts: '++id,first,last', 10 | emails: '++id,contactId,type,email', 11 | phones: '++id,contactId,type,phone', 12 | }); 13 | 14 | // Populate ground data 15 | db.on('populate', function () { 16 | 17 | // Populate a contact 18 | db.contacts.add({ first: 'Arnold', last: 'Fitzgerald' }).then(function (id) { 19 | 20 | // Populate some emails and phone numbers for the contact 21 | db.emails.add({ contactId: id, type: 'home', email: 'arnold@email.com' }); 22 | db.emails.add({ contactId: id, type: 'work', email: 'arnold@abc.com' }); 23 | db.phones.add({ contactId: id, type: 'home', phone: '12345678' }); 24 | db.phones.add({ contactId: id, type: 'work', phone: '987654321' }); 25 | }); 26 | }); 27 | 28 | // Open database 29 | db.open(); 30 | 31 | return db; 32 | }); 33 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/tools/build-configs/rollup.tests.config.js: -------------------------------------------------------------------------------- 1 | import sourcemaps from 'rollup-plugin-sourcemaps'; 2 | import commonjs from 'rollup-plugin-commonjs'; 3 | import nodeResolve from 'rollup-plugin-node-resolve'; 4 | 5 | const ERRORS_TO_IGNORE = [ 6 | "THIS_IS_UNDEFINED" 7 | ]; 8 | 9 | export default { 10 | input: 'tools/tmp/es5/test/addons/Dexie.Observable/test/unit/unit-tests-all.js', 11 | output: { 12 | file: 'test/unit/bundle.js', 13 | format: 'umd', 14 | globals: {dexie: "Dexie", "dexie-observable": "Dexie.Observable", QUnit: "QUnit"}, 15 | sourcemap: true, 16 | name: 'dexieTests' 17 | }, 18 | external: ['dexie', 'dexie-observable', 'QUnit'], 19 | plugins: [ 20 | sourcemaps(), 21 | nodeResolve({browser: true}), 22 | commonjs() 23 | ], 24 | onwarn ({loc, frame, code, message}) { 25 | if (ERRORS_TO_IGNORE.includes(code)) return; 26 | if ( loc ) { 27 | console.warn( `${loc.file} (${loc.line}:${loc.column}) ${message}` ); 28 | if ( frame ) console.warn( frame ); 29 | } else { 30 | console.warn(`${code} ${message}`); 31 | } 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/src/bulk-update.js: -------------------------------------------------------------------------------- 1 | import Dexie from 'dexie'; 2 | 3 | export default function bulkUpdate(table, changes) { 4 | let keys = changes.map(c => c.key); 5 | let map = {}; 6 | // Retrieve current object of each change to update and map each 7 | // found object's primary key to the existing object: 8 | return table.where(':id').anyOf(keys).raw().each((obj, cursor) => { 9 | map[cursor.primaryKey+''] = obj; 10 | }).then(()=>{ 11 | // Filter away changes whose key wasn't found in the local database 12 | // (we can't update them if we do not know the existing values) 13 | let updatesThatApply = changes.filter(c => map.hasOwnProperty(c.key+'')); 14 | // Apply modifications onto each existing object (in memory) 15 | // and generate array of resulting objects to put using bulkPut(): 16 | let objsToPut = updatesThatApply.map (c => { 17 | let curr = map[c.key+'']; 18 | Object.keys(c.mods).forEach(keyPath => { 19 | Dexie.setByKeyPath(curr, keyPath, c.mods[keyPath]); 20 | }); 21 | return curr; 22 | }); 23 | return table.bulkPut(objsToPut); 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/tools/build-configs/rollup.tests.unit.config.js: -------------------------------------------------------------------------------- 1 | import sourcemaps from 'rollup-plugin-sourcemaps'; 2 | import commonjs from 'rollup-plugin-commonjs'; 3 | import nodeResolve from 'rollup-plugin-node-resolve'; 4 | 5 | const ERRORS_TO_IGNORE = [ 6 | "THIS_IS_UNDEFINED" 7 | ]; 8 | 9 | export default { 10 | input: 'tools/tmp/es5/test/addons/Dexie.Observable/test/unit/unit-tests-all.js', 11 | output: { 12 | file: 'test/unit/bundle.js', 13 | format: 'umd', 14 | globals: {dexie: "Dexie", "dexie-observable": "Dexie.Observable", QUnit: "QUnit"}, 15 | sourcemap: true, 16 | name: 'dexieTests' 17 | }, 18 | external: ['dexie', 'dexie-observable', 'QUnit'], 19 | plugins: [ 20 | sourcemaps(), 21 | nodeResolve({browser: true}), 22 | commonjs() 23 | ], 24 | onwarn ({loc, frame, code, message}) { 25 | if (ERRORS_TO_IGNORE.includes(code)) return; 26 | if ( loc ) { 27 | console.warn( `${loc.file} (${loc.line}:${loc.column}) ${message}` ); 28 | if ( frame ) console.warn( frame ); 29 | } else { 30 | console.warn(`${code} ${message}`); 31 | } 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /samples/typescript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dexie-sample-typescript", 3 | "version": "1.0.0", 4 | "description": "Sample on how to use Dexie.js with Typescript", 5 | "scripts": { 6 | "build": "npm run build:ts && npm run build:rollup", 7 | "build:ts": "node node_modules/typescript/bin/tsc", 8 | "build:rollup": "node node_modules/rollup/bin/rollup -c", 9 | "clean": "rm -rf out", 10 | "test": "http-server . -a localhost -p 8081" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/dfahlander/Dexie.js.git" 15 | }, 16 | "keywords": [ 17 | "dexie", 18 | "samples" 19 | ], 20 | "author": "David Fahlander", 21 | "license": "Apache-2.0", 22 | "bugs": { 23 | "url": "https://github.com/dfahlander/Dexie.js/issues" 24 | }, 25 | "homepage": "https://github.com/dfahlander/Dexie.js#readme", 26 | "dependencies": { 27 | "dexie": "^2.0.0-beta.2" 28 | }, 29 | "devDependencies": { 30 | "rollup": "0.36.3", 31 | "rollup-plugin-commonjs": "5.0.5", 32 | "rollup-plugin-node-resolve": "2.0.0", 33 | "typescript": "2.1.0-dev.20161012" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /samples/angular2/src/test.ts: -------------------------------------------------------------------------------- 1 | import './polyfills.ts'; 2 | 3 | import 'zone.js/dist/long-stack-trace-zone'; 4 | import 'zone.js/dist/proxy.js'; 5 | import 'zone.js/dist/sync-test'; 6 | import 'zone.js/dist/jasmine-patch'; 7 | import 'zone.js/dist/async-test'; 8 | import 'zone.js/dist/fake-async-test'; 9 | import { getTestBed } from '@angular/core/testing'; 10 | import { 11 | BrowserDynamicTestingModule, 12 | platformBrowserDynamicTesting 13 | } from '@angular/platform-browser-dynamic/testing'; 14 | 15 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. 16 | declare var __karma__: any; 17 | declare var require: any; 18 | 19 | // Prevent Karma from running prematurely. 20 | __karma__.loaded = function () {}; 21 | 22 | // First, initialize the Angular testing environment. 23 | getTestBed().initTestEnvironment( 24 | BrowserDynamicTestingModule, 25 | platformBrowserDynamicTesting() 26 | ); 27 | // Then we find all the tests. 28 | let context = require.context('./', true, /\.spec\.ts$/); 29 | // And load the modules. 30 | context.keys().map(context); 31 | // Finally, start Karma to run the tests. 32 | __karma__.start(); 33 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/test/unit/tests-combine-create-and-update.js: -------------------------------------------------------------------------------- 1 | import {module, test, deepEqual, ok} from 'QUnit'; 2 | import combineCreateAndUpdate from '../../src/combine-create-and-update'; 3 | 4 | module('combineCreateAndUpdate', { 5 | setup: () => { 6 | }, 7 | teardown: () => { 8 | } 9 | }); 10 | 11 | test('should get a create change and update change and return a combined object', () => { 12 | const createChange = { 13 | obj: { 14 | foo: 'value', 15 | }, 16 | }; 17 | const updateChange = { 18 | mods: { 19 | foo: 'value2', 20 | bar: 'new Value', 21 | }, 22 | }; 23 | 24 | const res = combineCreateAndUpdate(createChange, updateChange); 25 | deepEqual(res.obj, { foo: 'value2', bar: 'new Value' }); 26 | }); 27 | 28 | test('should not change the original createObject', () => { 29 | const createChange = { 30 | obj: { 31 | foo: 'value', 32 | }, 33 | }; 34 | const updateChange = { 35 | mods: { 36 | foo: 'value2', 37 | bar: 'new Value', 38 | }, 39 | }; 40 | 41 | combineCreateAndUpdate(createChange, updateChange); 42 | deepEqual(createChange.obj, { foo: 'value' }); 43 | }); 44 | -------------------------------------------------------------------------------- /addons/dexie-export-import/tools/build-configs/rollup.tests.config.js: -------------------------------------------------------------------------------- 1 | import sourcemaps from 'rollup-plugin-sourcemaps'; 2 | import commonjs from 'rollup-plugin-commonjs'; 3 | import nodeResolve from 'rollup-plugin-node-resolve'; 4 | 5 | const ERRORS_TO_IGNORE = [ 6 | "THIS_IS_UNDEFINED" 7 | ]; 8 | 9 | export default { 10 | input: 'tools/tmp/test/index.js', 11 | output: [{ 12 | file: 'test/bundle.js', 13 | format: 'umd', 14 | globals: { 15 | dexie: "Dexie", 16 | qunit: "QUnit", 17 | "dexie-export-import": 18 | "DexieExportImport" 19 | }, 20 | name: 'DexieExportImport', 21 | sourcemap: true, 22 | exports: 'named' 23 | }], 24 | external: ['dexie', "qunit", "dexie-export-import"], 25 | plugins: [ 26 | sourcemaps(), 27 | nodeResolve({browser: true}), 28 | commonjs() 29 | ], 30 | onwarn ({loc, frame, code, message}) { 31 | if (ERRORS_TO_IGNORE.includes(code)) return; 32 | if ( loc ) { 33 | console.warn( `${loc.file} (${loc.line}:${loc.column}) ${message}` ); 34 | if ( frame ) console.warn( frame ); 35 | } else { 36 | console.warn(`${code} ${message}`); 37 | } 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /samples/electron/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/functions/make-class-constructor.ts: -------------------------------------------------------------------------------- 1 | import { arrayToObject, derive } from './utils'; 2 | 3 | 4 | export function makeClassConstructor (prototype: Object, constructor: Function) { 5 | /*const propertyDescriptorMap = arrayToObject( 6 | Object.getOwnPropertyNames(prototype), 7 | propKey => [propKey, Object.getOwnPropertyDescriptor(prototype, propKey)]); 8 | 9 | // Both derive and clone the prototype. 10 | // derive: So that x instanceof T returns true when T is the class template. 11 | // clone: Optimizes method access a bit (but actually not nescessary) 12 | const derivedPrototypeClone = Object.create(prototype, propertyDescriptorMap); 13 | derivedPrototypeClone.constructor = constructor; 14 | constructor.prototype = derivedPrototypeClone; 15 | return constructor as any as TConstructor;*/ 16 | 17 | // Keep the above code in case we want to clone AND derive the parent prototype. 18 | // Reason would be optimization of property access. 19 | // The code below will only create a prototypal inheritance from given constructor function 20 | // to given prototype. 21 | derive(constructor).from({prototype}); 22 | return constructor as any as TConstructor; 23 | } 24 | -------------------------------------------------------------------------------- /test/is-idb-and-promise-compatible.js: -------------------------------------------------------------------------------- 1 | import Dexie from 'dexie'; 2 | import {NativePromise} from '../src/helpers/promise'; 3 | 4 | var _resolve = NativePromise.resolve.bind(NativePromise); 5 | var _then = NativePromise.prototype.then; 6 | 7 | export class IdbPromiseIncompatibleError extends Error { 8 | constructor() { 9 | super("IndexedDB and Promise are incompatible on this browser"); 10 | this.name = "IdbPromiseIncompatibleError"; 11 | } 12 | } 13 | 14 | export async function isIdbAndPromiseCompatible() { 15 | let db = new Dexie("idbPromiseCompatTest"); 16 | db.version(1).stores({foo:'bar'}); 17 | return await db.transaction('r', db.foo, async ()=>{ 18 | let x = await db.foo.count(); 19 | let p = _resolve(0); 20 | for (let i=0;i<10;++i) { 21 | p = _then.call(p, x => x + 1); 22 | } 23 | let result = await p; 24 | console.log("Result: "+ result + " (should be 10"); 25 | try { 26 | await db.foo.count(); 27 | db.close(); 28 | return true; 29 | } catch (ex) { 30 | db.close(); 31 | throw new IdbPromiseIncompatibleError(); 32 | } 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/test/unit/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Include common configuration 2 | const {karmaCommon, getKarmaConfig, defaultBrowserMatrix} = require('../../../../test/karma.common'); 3 | 4 | module.exports = function (config) { 5 | const browserMatrixOverrides = { 6 | // Be fine with testing on local travis firefox + browserstack chrome, latest supported. 7 | ci: ["Firefox", "bs_chrome_latest_supported"], 8 | // This addon is not yet ready for full-blown tests on iphone/Safari. That's one of the reason it is still in beta. 9 | pre_npm_publish: defaultBrowserMatrix.pre_npm_publish.filter(b => !/bs_iphone7/i.test(b)) 10 | }; 11 | 12 | const cfg = getKarmaConfig(browserMatrixOverrides, { 13 | // Base path should point at the root 14 | basePath: '../../../../', 15 | files: karmaCommon.files.concat([ 16 | 'dist/dexie.js', 17 | 'addons/Dexie.Observable/dist/dexie-observable.js', 18 | 'addons/Dexie.Observable/test/unit/bundle.js', 19 | { pattern: 'addons/Dexie.Observable/test/unit/*.map', watched: false, included: false }, 20 | { pattern: 'addons/Dexie.Observable/dist/*.map', watched: false, included: false } 21 | ]) 22 | }); 23 | 24 | config.set(cfg); 25 | } 26 | -------------------------------------------------------------------------------- /test/integrations/dexie-relationships/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Include common configuration 2 | const {karmaCommon, getKarmaConfig, defaultBrowserMatrix} = require('../../karma.common'); 3 | 4 | module.exports = function (config) { 5 | const browserMatrixOverrides = { 6 | // Be fine with testing on local travis firefox for both pull requests and pushs. 7 | ci: ["Firefox"], 8 | // Be fine with chrome for this particular integration test. 9 | pre_npm_publish: ['bs_chrome_latest_supported'] 10 | }; 11 | 12 | const cfg = getKarmaConfig(browserMatrixOverrides, { 13 | // Base path should point at dexie root 14 | basePath: '../../../', 15 | // The files needed to apply dexie-observable to the standard dexie unit tests. 16 | files: karmaCommon.files.concat([ 17 | 'dist/dexie.js', // Dexie 18 | 'test/integrations/node_modules/dexie-relationships/dist/index.js', // dexieRelationships 19 | 'test/integrations/dexie-relationships/test-bundle.js', 20 | { pattern: 'test/integrations/dexie-relationships/test-bundle.js.map', included: false }, 21 | { pattern: 'test/integrations/node_modules/dexie-relationships/dist/*.map', included: false }, 22 | ]) 23 | }); 24 | 25 | config.set(cfg); 26 | } 27 | -------------------------------------------------------------------------------- /src/classes/dexie/vip.ts: -------------------------------------------------------------------------------- 1 | import { newScope } from '../../helpers/promise'; 2 | import { PSD } from '../../helpers/promise'; 3 | 4 | export function vip (fn) { 5 | // To be used by subscribers to the on('ready') event. 6 | // This will let caller through to access DB even when it is blocked while the db.ready() subscribers are firing. 7 | // This would have worked automatically if we were certain that the Provider was using Dexie.Promise for all asyncronic operations. The promise PSD 8 | // from the provider.connect() call would then be derived all the way to when provider would call localDatabase.applyChanges(). But since 9 | // the provider more likely is using non-promise async APIs or other thenable implementations, we cannot assume that. 10 | // Note that this method is only useful for on('ready') subscribers that is returning a Promise from the event. If not using vip() 11 | // the database could deadlock since it wont open until the returned Promise is resolved, and any non-VIPed operation started by 12 | // the caller will not resolve until database is opened. 13 | return newScope(function () { 14 | PSD.letThrough = true; // Make sure we are let through if still blocking db due to onready is firing. 15 | return fn(); 16 | }); 17 | } 18 | 19 | -------------------------------------------------------------------------------- /samples/react-redux/README.md: -------------------------------------------------------------------------------- 1 | # React + Redux + Dexie Todo Example 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app). 4 | 5 | *NOTE: This sample uses a beta version of Dexie* 6 | 7 | The [redux-thunk](https://github.com/gaearon/redux-thunk) middleware is used to handle the async calls of Dexie. 8 | 9 | ## Install dependencies 10 | 11 | Before you can run the app in your browser, you will have to install its dependencies with: 12 | 13 | ``` 14 | npm install 15 | ``` 16 | 17 | ## Development server 18 | 19 | Run `npm start` for a dev server. Navigate to `http://localhost:3000`. The app will automatically reload if you change any of the source files. You will also see any lint errors in the console. 20 | 21 | ## Build 22 | 23 | Run `npm run build` to build the project. The build artifacts will be stored in the `build/` directory. 24 | 25 | ## Further help 26 | 27 | * [Create React App User Guide](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md). 28 | * [Redux](http://redux.js.org/) 29 | * [react-redux](https://github.com/reactjs/react-redux) 30 | * To get more help on Dexie check out the [Dexie Wiki](https://github.com/dfahlander/Dexie.js/wiki) 31 | -------------------------------------------------------------------------------- /addons/dexie-export-import/src/tson-typed-array.ts: -------------------------------------------------------------------------------- 1 | declare var global; 2 | 3 | /* eslint-env browser, node */ 4 | import Typeson from 'typeson'; 5 | import {encode, decode} from 'base64-arraybuffer-es6'; 6 | 7 | const _global = typeof self === 'undefined' ? global : self; 8 | 9 | const exportObj = {}; 10 | [ 11 | 'Int8Array', 12 | 'Uint8Array', 13 | 'Uint8ClampedArray', 14 | 'Int16Array', 15 | 'Uint16Array', 16 | 'Int32Array', 17 | 'Uint32Array', 18 | 'Float32Array', 19 | 'Float64Array' 20 | ].forEach(function (typeName) { 21 | const arrType = typeName; 22 | const TypedArray = _global[arrType]; 23 | if (TypedArray) { 24 | exportObj[typeName.toLowerCase()+"2"] = { 25 | test (x) { return Typeson.toStringTag(x) === arrType; }, 26 | replace ({buffer, byteOffset, length}) { 27 | return { 28 | buffer, 29 | byteOffset, 30 | length 31 | }; 32 | }, 33 | revive (b64Obj) { 34 | const {buffer, byteOffset, length} = b64Obj; 35 | return new TypedArray(buffer, byteOffset, length); 36 | } 37 | }; 38 | } 39 | }); 40 | 41 | export default exportObj; 42 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { Dexie } from './classes/dexie'; 2 | import { DexieConstructor } from './public/types/dexie-constructor'; 3 | import { DexiePromise } from './helpers/promise'; 4 | import { mapError } from './errors'; 5 | import * as Debug from './helpers/debug'; 6 | import { dexieStackFrameFilter } from './globals/constants'; 7 | import { initDatabaseEnumerator } from './helpers/database-enumerator'; 8 | 9 | // Generate all static properties such as Dexie.maxKey etc 10 | // (implement interface DexieConstructor): 11 | import './classes/dexie/dexie-static-props'; 12 | 13 | // Init Database Enumerator (for Dexie.getDatabaseNames()) 14 | initDatabaseEnumerator((Dexie as any as DexieConstructor).dependencies.indexedDB); 15 | 16 | // Set rejectionMapper of DexiePromise so that it generally tries to map 17 | // DOMErrors and DOMExceptions to a DexieError instance with same name but with 18 | // async stack support and with a prototypal inheritance from DexieError and Error. 19 | // of Map DOMErrors and DOMExceptions to corresponding Dexie errors. 20 | DexiePromise.rejectionMapper = mapError; 21 | 22 | // Let the async stack filter focus on app code and filter away frames from dexie.min.js: 23 | Debug.setDebug(Debug.debug, dexieStackFrameFilter); 24 | 25 | export default Dexie; 26 | -------------------------------------------------------------------------------- /samples/react/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 16 | React App 17 | 18 | 19 |
    20 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /samples/react-redux/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 16 | React App 17 | 18 | 19 |
    20 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/public/types/database.d.ts: -------------------------------------------------------------------------------- 1 | import { Table } from "./table"; 2 | import { TransactionMode } from "./transaction-mode"; 3 | import { PromiseExtended } from "./promise-extended"; 4 | import { WhereClause } from "./where-clause"; 5 | import { Collection } from "./collection"; 6 | 7 | export interface Database { 8 | readonly name: string; 9 | readonly tables: Table[]; 10 | 11 | table(tableName: string): Table; 12 | 13 | transaction(mode: TransactionMode, table: Table, scope: () => PromiseLike | U): PromiseExtended; 14 | 15 | transaction(mode: TransactionMode, table: Table, table2: Table, scope: () => PromiseLike | U): PromiseExtended; 16 | 17 | transaction(mode: TransactionMode, table: Table, table2: Table, table3: Table, scope: () => PromiseLike | U): PromiseExtended; 18 | 19 | transaction(mode: TransactionMode, table: Table, table2: Table, table3: Table, table4: Table, scope: () => PromiseLike | U): PromiseExtended; 20 | 21 | transaction(mode: TransactionMode, table: Table, table2: Table, table3: Table, table4: Table, table5: Table, scope: () => PromiseLike | U): PromiseExtended; 22 | 23 | transaction(mode: TransactionMode, tables: Table[], scope: () => PromiseLike | U): PromiseExtended; 24 | } 25 | -------------------------------------------------------------------------------- /tools/build-configs/rollup.config.js: -------------------------------------------------------------------------------- 1 | import sourcemaps from 'rollup-plugin-sourcemaps'; 2 | import nodeResolve from 'rollup-plugin-node-resolve'; 3 | import cleanup from 'rollup-plugin-cleanup'; 4 | 5 | import {readFileSync} from 'fs'; 6 | import path from 'path'; 7 | 8 | const version = require(path.resolve(__dirname, '../../package.json')).version; 9 | 10 | export default { 11 | input: path.resolve(__dirname, '../../tools/tmp/src/index.js'), 12 | output: [{ 13 | file: path.resolve(__dirname, '../../dist/dexie.js'), 14 | format: 'umd', 15 | name: 'Dexie', 16 | globals: {}, // For tests, use "QUnit". For addons, use "Dexie" 17 | sourcemap: true, 18 | banner: readFileSync(path.resolve(__dirname, 'banner.txt'))+"" 19 | .replace(/{version}/g, version) 20 | .replace(/{date}/g, new Date().toDateString()), 21 | },{ 22 | file: path.resolve(__dirname, '../../dist/dexie.mjs'), 23 | format: 'es', 24 | sourcemap: true, 25 | banner: readFileSync(path.resolve(__dirname, 'banner.txt'))+"" 26 | .replace(/{version}/g, version) 27 | .replace(/{date}/g, new Date().toDateString()), 28 | }], 29 | plugins: [ 30 | sourcemaps(), 31 | nodeResolve({module: true, jsnext: true, browser: true, ignoreGlobal: false}), 32 | cleanup() 33 | ], 34 | }; 35 | -------------------------------------------------------------------------------- /addons/dexie-export-import/test/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Include common configuration 2 | const {karmaCommon, getKarmaConfig, defaultBrowserMatrix} = require('../../../test/karma.common'); 3 | 4 | module.exports = function (config) { 5 | const cfg = getKarmaConfig({ 6 | // I get error from browserstack/karma (not our code) when trying bs_iphone7. 7 | // If trying bs_safari it just times out. 8 | // Unit tests have been manually tested on Safari 12 though. 9 | ci: defaultBrowserMatrix.ci.filter(b => b !== 'bs_iphone7'), 10 | //local: ["bs_safari_latest_supported"], // Uncomment to use browserstack browsers from home 11 | // bs_iphone bails out before running any test at all. 12 | pre_npm_publish: defaultBrowserMatrix.pre_npm_publish.filter(b => !/bs_iphone7/i.test(b)) 13 | }, { 14 | // Base path should point at the root 15 | basePath: '../../../', 16 | files: karmaCommon.files.concat([ 17 | 'dist/dexie.js', 18 | 'addons/dexie-export-import/dist/dexie-export-import.js', 19 | 'addons/dexie-export-import/test/bundle.js', 20 | { pattern: 'addons/dexie-export-import/test/*.map', watched: false, included: false }, 21 | { pattern: 'addons/dexie-export-import/dist/*.map', watched: false, included: false } 22 | ]) 23 | }); 24 | 25 | config.set(cfg); 26 | } 27 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/src/hooks/deleting.js: -------------------------------------------------------------------------------- 1 | import {DELETE} from '../change_types'; 2 | 3 | export default function initDeletingHook(db, tableName) { 4 | return function deletingHook(primKey, obj, trans) { 5 | /// 6 | var promise = db._changes.add({ 7 | source: trans.source || null, // If a "source" is marked on the transaction, store it. Useful for observers that want to ignore their own changes. 8 | table: tableName, 9 | key: primKey, 10 | type: DELETE, 11 | oldObj: obj 12 | }).then(function (rev) { 13 | trans._lastWrittenRevision = Math.max(trans._lastWrittenRevision, rev); 14 | return rev; 15 | }) 16 | .catch((e) => { 17 | console.log(obj) 18 | console.log(e.stack) 19 | }) 20 | this.onerror = function () { 21 | // If the main operation fails, make sure to regret the change. 22 | // Using _then because if promise is already fullfilled, the standard then() would 23 | // do setTimeout() and we would loose the transaction. 24 | promise._then(function (rev) { 25 | // Will only happen if app code catches the main operation error to prohibit transaction from aborting. 26 | db._changes.delete(rev); 27 | }); 28 | }; 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/src/get-local-changes-for-node/get-base-revision-and-max-client-revision.js: -------------------------------------------------------------------------------- 1 | export default function getBaseRevisionAndMaxClientRevision(node) { 2 | /// 3 | if (node.remoteBaseRevisions.length === 0) 4 | return { 5 | // No remoteBaseRevisions have arrived yet. No limit on clientRevision and provide null as remoteBaseRevision: 6 | maxClientRevision: Infinity, 7 | remoteBaseRevision: null 8 | }; 9 | for (var i = node.remoteBaseRevisions.length - 1; i >= 0; --i) { 10 | if (node.myRevision >= node.remoteBaseRevisions[i].local) { 11 | // Found a remoteBaseRevision that fits node.myRevision. Return remoteBaseRevision and eventually a roof maxClientRevision pointing out where next remoteBaseRevision bases its changes on. 12 | return { 13 | maxClientRevision: i === node.remoteBaseRevisions.length - 1 ? Infinity : node.remoteBaseRevisions[i + 1].local, 14 | remoteBaseRevision: node.remoteBaseRevisions[i].remote 15 | }; 16 | } 17 | } 18 | // There are at least one item in the list but the server hasn't yet become up-to-date with the 0 revision from client. 19 | return { 20 | maxClientRevision: node.remoteBaseRevisions[0].local, 21 | remoteBaseRevision: null 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /src/public/types/table-hooks.d.ts: -------------------------------------------------------------------------------- 1 | import { DexieEventSet } from "./dexie-event-set"; 2 | import { DexieEvent } from "./dexie-event"; 3 | import { Transaction } from "./transaction"; 4 | import { IndexableType } from "./indexable-type"; 5 | 6 | interface CreatingHookContext { 7 | onsuccess?: (primKey: Key) => void; 8 | onerror?: (err: any) => void; 9 | } 10 | 11 | interface UpdatingHookContext { 12 | onsuccess?: (updatedObj: T) => void; 13 | onerror?: (err: any) => void; 14 | } 15 | 16 | interface DeletingHookContext { 17 | onsuccess?: () => void; 18 | onerror?: (err: any) => void; 19 | } 20 | 21 | interface TableHooks extends DexieEventSet { 22 | (eventName: 'creating', subscriber: (this: CreatingHookContext, primKey:TKey, obj:T, transaction:Transaction) => any): void; 23 | (eventName: 'reading', subscriber: (obj:T) => T | any): void; 24 | (eventName: 'updating', subscriber: (this: UpdatingHookContext, modifications:Object, primKey:TKey, obj:T, transaction:Transaction) => any): void; 25 | (eventName: 'deleting', subscriber: (this: DeletingHookContext, primKey:TKey, obj:T, transaction:Transaction) => any): void; 26 | creating: DexieEvent; 27 | reading: DexieEvent; 28 | updating: DexieEvent; 29 | deleting: DexieEvent; 30 | } 31 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/src/on-storage.js: -------------------------------------------------------------------------------- 1 | import Dexie from 'dexie'; 2 | 3 | export default function initOnStorage(Observable) { 4 | return function onStorage(event) { 5 | // We use the onstorage event to trigger onLatestRevisionIncremented since we will wake up when other windows modify the DB as well! 6 | if (event.key.indexOf("Dexie.Observable/") === 0) { // For example "Dexie.Observable/latestRevision/FriendsDB" 7 | var parts = event.key.split('/'); 8 | var prop = parts[1]; 9 | var dbname = parts[2]; 10 | if (prop === 'latestRevision') { 11 | var rev = parseInt(event.newValue, 10); 12 | if (!isNaN(rev) && rev > Observable.latestRevision[dbname]) { 13 | Observable.latestRevision[dbname] = rev; 14 | Dexie.ignoreTransaction(function () { 15 | Observable.on('latestRevisionIncremented').fire(dbname, rev); 16 | }); 17 | } 18 | } else if (prop.indexOf("deadnode:") === 0) { 19 | var nodeID = parseInt(prop.split(':')[1], 10); 20 | if (event.newValue) { 21 | Observable.on.suicideNurseCall.fire(dbname, nodeID); 22 | } 23 | } else if (prop === 'intercomm') { 24 | if (event.newValue) { 25 | Observable.on.intercomm.fire(dbname); 26 | } 27 | } 28 | } 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /addons/dexie-export-import/src/dexie-export-import.ts: -------------------------------------------------------------------------------- 1 | import Dexie from 'dexie'; 2 | import { ExportOptions, exportDB } from './export'; 3 | import { importDB, ImportOptions, importInto, StaticImportOptions } from './import'; 4 | 5 | export { exportDB, ExportOptions}; 6 | export { importDB, importInto, ImportOptions}; 7 | 8 | // 9 | // Extend Dexie interface (typescript-wise) 10 | // 11 | declare module 'dexie' { 12 | // Extend methods on db 13 | interface Dexie { 14 | export(options?: ExportOptions): Promise; 15 | import(blob: Blob, options?: ImportOptions): Promise; 16 | } 17 | interface DexieConstructor { 18 | import(blob: Blob, options?: StaticImportOptions): Promise; 19 | } 20 | } 21 | 22 | // 23 | // Extend Dexie interface (runtime wise) 24 | // 25 | 26 | Dexie.prototype.export = function (this: Dexie, options?: ExportOptions) { 27 | return exportDB(this, options); 28 | }; 29 | Dexie.prototype.import = function (this: Dexie, blob: Blob, options?: ImportOptions) { 30 | return importInto(this, blob, options); 31 | }; 32 | Dexie.import = (blob: Blob, options?: StaticImportOptions) => importDB(blob, options); 33 | 34 | export default ()=>{ 35 | throw new Error("This addon extends Dexie.prototype globally and does not have be included in Dexie constructor's addons options.") 36 | }; 37 | -------------------------------------------------------------------------------- /samples/angular2/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/0.13/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', 'angular-cli'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-remap-istanbul'), 12 | require('angular-cli/plugins/karma') 13 | ], 14 | files: [ 15 | { pattern: './src/test.ts', watched: false } 16 | ], 17 | preprocessors: { 18 | './src/test.ts': ['angular-cli'] 19 | }, 20 | mime: { 21 | 'text/x-typescript': ['ts','tsx'] 22 | }, 23 | remapIstanbulReporter: { 24 | reports: { 25 | html: 'coverage', 26 | lcovonly: './coverage/coverage.lcov' 27 | } 28 | }, 29 | angularCli: { 30 | config: './angular-cli.json', 31 | environment: 'dev' 32 | }, 33 | reporters: config.angularCli && config.angularCli.codeCoverage 34 | ? ['progress', 'karma-remap-istanbul'] 35 | : ['progress'], 36 | port: 9876, 37 | colors: true, 38 | logLevel: config.LOG_INFO, 39 | autoWatch: true, 40 | browsers: ['Chrome'], 41 | singleRun: false 42 | }); 43 | }; 44 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/src/enqueue.js: -------------------------------------------------------------------------------- 1 | import Dexie from 'dexie'; 2 | 3 | export default function initEnqueue(db) { 4 | return function enqueue(context, fn, instanceID) { 5 | function _enqueue() { 6 | if (!context.ongoingOperation) { 7 | context.ongoingOperation = Dexie.ignoreTransaction(function () { 8 | return Dexie.vip(function () { 9 | return fn(); 10 | }); 11 | }).finally(()=> { 12 | delete context.ongoingOperation; 13 | }); 14 | } else { 15 | context.ongoingOperation = context.ongoingOperation.then(function () { 16 | return enqueue(context, fn, instanceID); 17 | }); 18 | } 19 | return context.ongoingOperation; 20 | } 21 | 22 | if (!instanceID) { 23 | // Caller wants to enqueue it until database becomes open. 24 | if (db.isOpen()) { 25 | return _enqueue(); 26 | } else { 27 | return Dexie.Promise.reject(new Dexie.DatabaseClosedError()); 28 | } 29 | } else if (db._localSyncNode && instanceID === db._localSyncNode.id) { 30 | // DB is already open but queue doesn't want it to be queued if database has been closed (request bound to current instance of DB) 31 | return _enqueue(); 32 | } else { 33 | return Dexie.Promise.reject(new Dexie.DatabaseClosedError()); 34 | } 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/test/integration/test-syncable-dexie-tests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Dexie Unit tests with Dexie.Syncable applied and dummy ISyncProtocol 6 | 7 | 8 | 9 | 10 |
    11 |
    12 | 13 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/api.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * API for Dexie.Observable. 3 | * 4 | * Contains interfaces used by dexie-observable. 5 | * 6 | * By separating module 'dexie-observable' from 'dexie-observable/api' we 7 | * distinguish that: 8 | * 9 | * import {...} from 'dexie-observable/api' is only for getting access to its 10 | * interfaces and has no side-effects. 11 | * Typescript-only import. 12 | * 13 | * import 'dexie-observable' is only for side effects - to extend Dexie with 14 | * functionality of dexie-observable. 15 | * Javascript / Typescript import. 16 | * 17 | */ 18 | export const enum DatabaseChangeType { 19 | Create = 1, 20 | Update = 2, 21 | Delete = 3 22 | } 23 | 24 | export interface ICreateChange { 25 | type: DatabaseChangeType.Create, 26 | table: string; 27 | key: any; 28 | obj: any; 29 | } 30 | 31 | export interface IUpdateChange { 32 | type: DatabaseChangeType.Update; 33 | table: string; 34 | key: any; 35 | mods: {[keyPath: string]:any | undefined}; 36 | } 37 | 38 | export interface IDeleteChange { 39 | type: DatabaseChangeType.Delete; 40 | table: string; 41 | key: any; 42 | } 43 | 44 | export type IDatabaseChange = ICreateChange | IUpdateChange | IDeleteChange; 45 | -------------------------------------------------------------------------------- /samples/angular2/README.md: -------------------------------------------------------------------------------- 1 | # Ng2dexie 2 | 3 | This project was generated with [angular-cli](https://github.com/angular/angular-cli) version 1.0.0-beta.24. Currently there are no E2E or unit tests in this sample project. The e2e and unit tests files were kept to make it easier to copy this project and extend it without having to later add configuration files for karma, protractor etc. 4 | 5 | *NOTE: This sample uses a beta version of Dexie* 6 | 7 | ## Install dependencies 8 | 9 | Before you can run the app in your browser, you will have to install its dependencies with: 10 | 11 | ``` 12 | npm install 13 | ``` 14 | 15 | ## Development server 16 | 17 | Run `npm start` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 18 | You can also install `angular-cli` globally and use `ng serve`. 19 | 20 | ## Build 21 | 22 | Run `npm run ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build. 23 | You can also install `angular-cli` globally and use `ng build`. 24 | 25 | ## Further help 26 | 27 | To get more help on the `angular-cli` use `ng help` or go check out the [Angular-CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 28 | To get more help on Dexie check out the [Dexie Wiki](https://github.com/dfahlander/Dexie.js/wiki) 29 | -------------------------------------------------------------------------------- /samples/angular2/angular-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "project": { 3 | "version": "1.0.0-beta.24", 4 | "name": "ng2dexie" 5 | }, 6 | "apps": [ 7 | { 8 | "root": "src", 9 | "outDir": "dist", 10 | "assets": [ 11 | "assets", 12 | "favicon.ico" 13 | ], 14 | "index": "index.html", 15 | "main": "main.ts", 16 | "test": "test.ts", 17 | "tsconfig": "tsconfig.json", 18 | "prefix": "app", 19 | "mobile": false, 20 | "styles": [ 21 | "styles.css" 22 | ], 23 | "scripts": [], 24 | "environments": { 25 | "source": "environments/environment.ts", 26 | "dev": "environments/environment.ts", 27 | "prod": "environments/environment.prod.ts" 28 | } 29 | } 30 | ], 31 | "addons": [], 32 | "packages": [], 33 | "e2e": { 34 | "protractor": { 35 | "config": "./protractor.conf.js" 36 | } 37 | }, 38 | "test": { 39 | "karma": { 40 | "config": "./karma.conf.js" 41 | } 42 | }, 43 | "defaults": { 44 | "styleExt": "css", 45 | "prefixInterfaces": false, 46 | "inline": { 47 | "style": false, 48 | "template": false 49 | }, 50 | "spec": { 51 | "class": false, 52 | "component": true, 53 | "directive": true, 54 | "module": false, 55 | "pipe": true, 56 | "service": true 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /samples/react-redux/src/actions/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | LOAD_TODOS, 3 | ADD_TODO, 4 | UPDATE_TODO, 5 | DELETE_TODO, 6 | } from '../constants'; 7 | import db from '../db'; 8 | 9 | export function loadTodos() { 10 | return (dispatch) => { 11 | db.table('todos') 12 | .toArray() 13 | .then((todos) => { 14 | dispatch({ 15 | type: LOAD_TODOS, 16 | payload: todos, 17 | }); 18 | }); 19 | }; 20 | } 21 | 22 | export function addTodo(title) { 23 | return (dispatch) => { 24 | const todoToAdd = { title, done: false }; 25 | db.table('todos') 26 | .add(todoToAdd) 27 | .then((id) => { 28 | dispatch({ 29 | type: ADD_TODO, 30 | payload: Object.assign({}, todoToAdd, { id }), 31 | }); 32 | }); 33 | } 34 | } 35 | 36 | export function deleteTodo(id) { 37 | return (dispatch) => { 38 | db.table('todos') 39 | .delete(id) 40 | .then(() => { 41 | dispatch({ 42 | type: DELETE_TODO, 43 | payload: id, 44 | }); 45 | }); 46 | }; 47 | } 48 | 49 | export function updateTodo(id, done) { 50 | return (dispatch) => { 51 | db.table('todos') 52 | .update(id, { done }) 53 | .then(() => { 54 | dispatch({ 55 | type: UPDATE_TODO, 56 | payload: { id, done }, 57 | }); 58 | }); 59 | }; 60 | } 61 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/src/override-parse-stores-spec.js: -------------------------------------------------------------------------------- 1 | export default function overrideParseStoresSpec(origFunc) { 2 | return function(stores, dbSchema) { 3 | // Create the _changes and _syncNodes tables 4 | stores["_changes"] = "++rev"; 5 | stores["_syncNodes"] = "++id,myRevision,lastHeartBeat,&url,isMaster,type,status"; 6 | stores["_intercomm"] = "++id,destinationNode"; 7 | stores["_uncommittedChanges"] = "++id,node"; // For remote syncing when server returns a partial result. 8 | // Call default implementation. Will populate the dbSchema structures. 9 | origFunc.call(this, stores, dbSchema); 10 | // Allow UUID primary keys using $$ prefix on primary key or indexes 11 | Object.keys(dbSchema).forEach(function(tableName) { 12 | var schema = dbSchema[tableName]; 13 | if (schema.primKey.name.indexOf('$$') === 0) { 14 | schema.primKey.uuid = true; 15 | schema.primKey.name = schema.primKey.name.substr(2); 16 | schema.primKey.keyPath = schema.primKey.keyPath.substr(2); 17 | } 18 | }); 19 | // Now mark all observable tables 20 | Object.keys(dbSchema).forEach(function(tableName) { 21 | // Marked observable tables with "observable" in their TableSchema. 22 | if (tableName.indexOf('_') !== 0 && tableName.indexOf('$') !== 0) { 23 | dbSchema[tableName].observable = true; 24 | } 25 | }); 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /test/integrations/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dexie-integration-tests", 3 | "version": "1.0.0", 4 | "description": "A collection of integration tests to run as part of CI to ensure dexie keep up the compatibility with 3rd part addons and other work that depends on dexie", 5 | "main": "tests-bundle.js", 6 | "module": "index.js", 7 | "scripts": { 8 | "test": "npm run test:dexie-relationships", 9 | "test:dexie-relationships": "just-build dexie-relationships" 10 | }, 11 | "just-build": { 12 | "dexie-relationships": [ 13 | "# Transpile JS to ES5", 14 | "tsc --allowJs --moduleResolution node --lib es2015,dom -t es5 -m es2015 --outDir tmp/es5 --rootDir .. --sourceMap dexie-relationships/index.js [--watch 'Compilation complete.']", 15 | "# Generate UMD module bundle", 16 | "rollup -c dexie-relationships/rollup.config.js", 17 | "# Start the tests", 18 | "karma start dexie-relationships/karma.conf.js --single-run" 19 | ] 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "git+https://github.com/dfahlander/Dexie.js.git" 24 | }, 25 | "author": "", 26 | "license": "Apache-2.0", 27 | "bugs": { 28 | "url": "https://github.com/dfahlander/Dexie.js/issues" 29 | }, 30 | "homepage": "https://github.com/dfahlander/Dexie.js#readme", 31 | "dependencies": { 32 | "just-build": "0.9.16", 33 | "dexie-relationships": "1.2.1" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/src/wakeup-observers.js: -------------------------------------------------------------------------------- 1 | import Dexie from 'dexie'; 2 | 3 | export default function initWakeupObservers(db, Observable, localStorage) { 4 | return function wakeupObservers(lastWrittenRevision) { 5 | // Make sure Observable.latestRevision[db.name] is still below our value, now when some time has elapsed and other db instances in same window possibly could have made changes too. 6 | if (Observable.latestRevision[db.name] < lastWrittenRevision) { 7 | // Set the static property lastRevision[db.name] to the revision of the last written change. 8 | Observable.latestRevision[db.name] = lastWrittenRevision; 9 | // Wakeup ourselves, and any other db instances on this window: 10 | Dexie.ignoreTransaction(function () { 11 | Observable.on('latestRevisionIncremented').fire(db.name, lastWrittenRevision); 12 | }); 13 | // Observable.on.latestRevisionIncremented will only wakeup db's in current window. 14 | // We need a storage event to wakeup other windwos. 15 | // Since indexedDB lacks storage events, let's use the storage event from WebStorage just for 16 | // the purpose to wakeup db instances in other windows. 17 | if (localStorage) localStorage.setItem('Dexie.Observable/latestRevision/' + db.name, lastWrittenRevision); // In IE, this will also wakeup our own window. However, onLatestRevisionIncremented will work around this by only running once per revision id. 18 | } 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/dist/README.md: -------------------------------------------------------------------------------- 1 | ## Can't find dexie-syncable.js? 2 | Transpiled code (dist version) IS ONLY checked in to 3 | the [releases](https://github.com/dfahlander/Dexie.js/tree/releases/addons/Dexie.Syncable/dist). 4 | branch. 5 | 6 | ## Download 7 | [unpkg.com/dexie-syncable/dist/dexie-syncable.js](https://unpkg.com/dexie-syncable/dist/dexie-syncable.js) 8 | 9 | [unpkg.com/dexie-syncable/dist/dexie-syncable.min.js](https://unpkg.com/dexie-syncable/dist/dexie-syncable.min.js) 10 | 11 | [unpkg.com/dexie-syncable/dist/dexie-syncable.js.map](https://unpkg.com/dexie-syncable/dist/dexie-syncable.js.map) 12 | 13 | [unpkg.com/dexie-syncable/dist/dexie-syncable.min.js.map](https://unpkg.com/dexie-syncable/dist/dexie-syncable.min.js.map) 14 | 15 | ## npm 16 | ``` 17 | npm install dexie-syncable --save 18 | ``` 19 | ## bower 20 | Since Dexie v1.3.4, addons are included in the dexie bower package. 21 | ``` 22 | $ bower install dexie --save 23 | $ ls bower_components/dexie/addons/Dexie.Syncable/dist 24 | dexie-syncable.js dexie-syncable.js.map dexie-syncable.min.js dexie-syncable.min.js.map 25 | 26 | ``` 27 | ## Or build them yourself... 28 | Fork Dexie.js, then: 29 | ``` 30 | git clone https://github.com/YOUR-USERNAME/Dexie.js.git 31 | cd Dexie.js 32 | npm install 33 | cd addons/Dexie.Syncable 34 | npm run build # or npm run watch 35 | 36 | ``` 37 | If you're on windows, you need to use an elevated command prompt of some reason to get `npm install` to work. 38 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/src/combine-update-and-update.js: -------------------------------------------------------------------------------- 1 | import Dexie from 'dexie'; 2 | 3 | export default function combineUpdateAndUpdate(prevChange, nextChange) { 4 | var clonedChange = Dexie.deepClone(prevChange); // Clone object before modifying since the earlier change in db.changes[] would otherwise be altered. 5 | Object.keys(nextChange.mods).forEach(function (keyPath) { 6 | // If prev-change was changing a parent path of this keyPath, we must update the parent path rather than adding this keyPath 7 | var hadParentPath = false; 8 | Object.keys(prevChange.mods).filter(function (parentPath) { return keyPath.indexOf(parentPath + '.') === 0; }).forEach(function (parentPath) { 9 | Dexie.setByKeyPath(clonedChange.mods[parentPath], keyPath.substr(parentPath.length + 1), nextChange.mods[keyPath]); 10 | hadParentPath = true; 11 | }); 12 | if (!hadParentPath) { 13 | // Add or replace this keyPath and its new value 14 | clonedChange.mods[keyPath] = nextChange.mods[keyPath]; 15 | } 16 | // In case prevChange contained sub-paths to the new keyPath, we must make sure that those sub-paths are removed since 17 | // we must mimic what would happen if applying the two changes after each other: 18 | Object.keys(prevChange.mods).filter(function (subPath) { return subPath.indexOf(keyPath + '.') === 0; }).forEach(function (subPath) { 19 | delete clonedChange.mods[subPath]; 20 | }); 21 | }); 22 | return clonedChange; 23 | } 24 | -------------------------------------------------------------------------------- /test/karma.browsers.matrix.js: -------------------------------------------------------------------------------- 1 | /** This module comprises the list of browsers 2 | * to run tests on depending on environment. 3 | * 4 | * Browsers listed here must also be defined in 5 | * karma.browserstack.js 6 | */ 7 | 8 | module.exports = { 9 | // On developers machines, Chrome is most likely to be installed. 10 | local: ['Chrome'], 11 | //local: ['bs_safari_latest_supported'], 12 | 13 | // When browserstack cannot be used, use local Firefox. 14 | ciLocal: ['Firefox'], 15 | 16 | // Continous Integration on every push to master 17 | ci: [ 18 | // - Let firefox represent the standard evergreen browser. 19 | // Leaving out Chrome, since local tests have hopefully already run on it. 20 | // Chrome will be tested in the pre_npm_publish anyway. 21 | 'bs_firefox_latest_supported', 22 | // Internet Explorer - an old beast. Enforces legacy compatibility for every PR! 23 | 'bs_ie11', 24 | // Safari 10.1 - another beast. Enforces native Safari support for every PR! 25 | 'bs_safari_latest_supported' 26 | ], 27 | 28 | // Test matrix used before every npm publish. 29 | pre_npm_publish: [ 30 | 'bs_chrome_oldest_supported', 31 | 'bs_chrome_latest_supported', 32 | 'bs_firefox_oldest_supported', 33 | 'bs_firefox_latest_supported', 34 | "bs_iphone7", // Safari 10.1 on iOS 10.3 35 | "bs_safari_latest_supported" // Safari 12 on Mojave 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /test/typings-test/test-extend-dexie.ts: -------------------------------------------------------------------------------- 1 | 2 | import {Dexie, Table, DexieConstructor, IndexableType} from '../../dist/dexie'; 3 | 4 | // 5 | // Extend Dexie interface 6 | // 7 | declare module '../../dist/dexie' { 8 | interface Table { 9 | extendedTableMethod() : any; 10 | } 11 | interface DbEvents { 12 | (eventName: 'changes', subscriber: ()=>any): void; 13 | (eventName: 'customEvent2', subscriber: ()=>any): void; 14 | changes: DexieEvent; 15 | customEvent2: DexieEvent; 16 | } 17 | interface DexieConstructor { 18 | extendedStaticMethod (param1: string) : string; 19 | } 20 | 21 | interface Dexie { 22 | extendedDBMethod() : any; 23 | } 24 | } 25 | 26 | Dexie.addons.push(db => { 27 | db.Table.prototype.extendedTableMethod = ()=>{}; 28 | db.extendedDBMethod = ()=>{}; 29 | db.on.addEventType({ 30 | changes: 'asap' 31 | }); 32 | db.on.addEventType('customEvent2', (a,b)=>()=>{a(); b();}, ()=>{}); 33 | }); 34 | 35 | Dexie.extendedStaticMethod = param1 => param1; 36 | 37 | 38 | //var x: Dexie.Table<{name: string, age: number}, number>; 39 | var db: Dexie = null as any as Dexie; 40 | 41 | var x: Dexie.Promise = null as any as Dexie.Promise; 42 | var x2 = Dexie.Promise.all([1]); 43 | x = x2; 44 | 45 | 46 | var y: Dexie.Table<{hello: any}, number>; 47 | var y2: Table<{hello: any}, number>; 48 | var y3 = db.table<{hello: any}, number>("hello"); 49 | y2 = y3; 50 | y = y2; 51 | 52 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/test/integration/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Include common configuration 2 | const {karmaCommon, getKarmaConfig, defaultBrowserMatrix} = require('../../../../test/karma.common'); 3 | 4 | module.exports = function (config) { 5 | const browserMatrixOverrides = { 6 | // Be fine with testing on local travis firefox + browserstack chrome, latest supported. 7 | ci: ["Firefox", "bs_chrome_latest_supported"], 8 | // This addon is not yet ready for full-blown tests on iphone/Safari. That's one of the reason it is still in beta. 9 | // Firefox 55 has bug that is triggered when addons are present. Bug is fixed for FF57: https://bugzilla.mozilla.org/show_bug.cgi?id=1395071 10 | pre_npm_publish: defaultBrowserMatrix.pre_npm_publish.filter(b => 11 | !/bs_iphone7|bs_firefox_latest_supported/i.test(b)) 12 | }; 13 | 14 | const cfg = getKarmaConfig(browserMatrixOverrides, { 15 | // Base path should point at the root 16 | basePath: '../../../../', 17 | // The files needed to apply dexie-observable to the standard dexie unit tests. 18 | files: karmaCommon.files.concat([ 19 | 'dist/dexie.js', 20 | 'addons/Dexie.Observable/test/integration/karma-env.js', 21 | 'addons/Dexie.Observable/dist/dexie-observable.js', // Apply observable addon 22 | 'test/bundle.js', // The dexie standard test suite 23 | { pattern: 'addons/Dexie.Observable/dist/*.map', watched: false, included: false } 24 | ]) 25 | }); 26 | 27 | config.set(cfg); 28 | } 29 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/test/unit/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Include common configuration 2 | const {karmaCommon, getKarmaConfig, defaultBrowserMatrix} = require('../../../../test/karma.common'); 3 | 4 | module.exports = function (config) { 5 | const browserMatrixOverrides = { 6 | // Be fine with testing on local travis firefox + browserstack chrome, latest supported. 7 | ci: ["Firefox", "bs_chrome_latest_supported"], 8 | // This addon is not yet ready for full-blown tests on iphone/Safari. That's one of the reason it is still in beta. 9 | pre_npm_publish: defaultBrowserMatrix.pre_npm_publish.filter(b => !/bs_iphone7/i.test(b)) 10 | }; 11 | 12 | const cfg = getKarmaConfig(browserMatrixOverrides, { 13 | // Base path should point at the root 14 | basePath: '../../../../', 15 | files: karmaCommon.files.concat([ 16 | 'dist/dexie.js', 17 | 'addons/Dexie.Observable/dist/dexie-observable.js', 18 | 19 | 'samples/remote-sync/websocket/websocketserver-shim.js', 20 | 'samples/remote-sync/websocket/WebSocketSyncServer.js',// With shim applied, we can run the server in the browser 21 | 22 | 'addons/Dexie.Syncable/test/unit/bundle.js', 23 | { pattern: 'addons/Dexie.Observable/dist/*.map', watched: false, included: false }, 24 | { pattern: 'addons/Dexie.Syncable/dist/*.map', watched: false, included: false }, 25 | { pattern: 'addons/Dexie.Syncable/test/unit/*.map', watched: false, included: false }, 26 | ]) 27 | }); 28 | 29 | config.set(cfg); 30 | } 31 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/test/unit/tests-register-sync-protocol.js: -------------------------------------------------------------------------------- 1 | import Dexie from 'dexie'; 2 | import '../../src/Dexie.Syncable'; 3 | import {module, test, strictEqual, raises} from 'QUnit'; 4 | 5 | module('registerSyncProtocol', { 6 | setup: () => { 7 | }, 8 | teardown: () => { 9 | } 10 | }); 11 | 12 | test('should set partialsThreshold to Infinity if no threshold was given', () => { 13 | const protocolName = 'foo'; 14 | Dexie.Syncable.registerSyncProtocol(protocolName, { 15 | sync() {}, 16 | }); 17 | 18 | strictEqual(Dexie.Syncable.registeredProtocols[protocolName].partialsThreshold, Infinity); 19 | }); 20 | 21 | test('should save the given partialsThreshold', () => { 22 | const protocolName = 'foo'; 23 | Dexie.Syncable.registerSyncProtocol(protocolName, { 24 | sync() {}, 25 | partialsThreshold: 1000 26 | }); 27 | 28 | strictEqual(Dexie.Syncable.registeredProtocols[protocolName].partialsThreshold, 1000); 29 | }); 30 | 31 | test('should throw an error if the partialsThreshold is NaN or smaller 0', () => { 32 | const protocolName = 'foo'; 33 | 34 | function fn1() { 35 | Dexie.Syncable.registerSyncProtocol(protocolName, { 36 | sync() {}, 37 | partialsThreshold: NaN 38 | }); 39 | } 40 | 41 | raises(fn1, Error, 'NaN test'); 42 | 43 | function fn2() { 44 | Dexie.Syncable.registerSyncProtocol(protocolName, { 45 | sync() {}, 46 | partialsThreshold: -10 47 | }); 48 | } 49 | 50 | raises(fn2, Error, 'Negative number test'); 51 | }); 52 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/dist/README.md: -------------------------------------------------------------------------------- 1 | ## Can't find dexie-observable.js? 2 | Transpiled code (dist version) IS ONLY checked in to 3 | the [releases](https://github.com/dfahlander/Dexie.js/tree/releases/addons/Dexie.Observable/dist) 4 | branch. 5 | 6 | ## Download 7 | [unpkg.com/dexie-observable/dist/dexie-observable.js](https://unpkg.com/dexie-observable/dist/dexie-observable.js) 8 | 9 | [unpkg.com/dexie-observable/dist/dexie-observable.min.js](https://unpkg.com/dexie-observable/dist/dexie-observable.min.js) 10 | 11 | [unpkg.com/dexie-observable/dist/dexie-observable.js.map](https://unpkg.com/dexie-observable/dist/dexie-observable.js.map) 12 | 13 | [unpkg.com/dexie-observable/dist/dexie-observable.min.js.map](https://unpkg.com/dexie-observable/dist/dexie-observable.min.js.map) 14 | 15 | ## npm 16 | ``` 17 | npm install dexie-observable --save 18 | ``` 19 | ## bower 20 | Since Dexie v1.3.4, addons are included in the dexie bower package. 21 | ``` 22 | $ bower install dexie --save 23 | $ ls bower_components/dexie/addons/Dexie.Observable/dist 24 | dexie-observable.js dexie-observable.js.map dexie-observable.min.js dexie-observable.min.js.map 25 | 26 | ``` 27 | ## Or build them yourself... 28 | Fork Dexie.js, then: 29 | ``` 30 | git clone https://github.com/YOUR-USERNAME/Dexie.js.git 31 | cd Dexie.js 32 | npm install 33 | cd addons/Dexie.Observable 34 | npm run build # or npm run watch 35 | 36 | ``` 37 | If you're on windows, you need to use an elevated command prompt of some reason to get `npm install` to work. 38 | -------------------------------------------------------------------------------- /samples/angular2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ng2dexie", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "angular-cli": {}, 6 | "scripts": { 7 | "ng": "ng", 8 | "start": "ng serve", 9 | "lint": "tslint \"src/**/*.ts\"", 10 | "test": "ng test", 11 | "pree2e": "webdriver-manager update --standalone false --gecko false", 12 | "e2e": "protractor" 13 | }, 14 | "private": true, 15 | "dependencies": { 16 | "@angular/common": "^2.3.1", 17 | "@angular/compiler": "^2.3.1", 18 | "@angular/core": "^2.3.1", 19 | "@angular/forms": "^2.3.1", 20 | "@angular/http": "^2.3.1", 21 | "@angular/platform-browser": "^2.3.1", 22 | "@angular/platform-browser-dynamic": "^2.3.1", 23 | "@angular/router": "^3.3.1", 24 | "core-js": "^2.4.1", 25 | "dexie": "^2.0.0-beta.7", 26 | "rxjs": "^5.0.1", 27 | "ts-helpers": "^1.1.1", 28 | "zone.js": "^0.7.2" 29 | }, 30 | "devDependencies": { 31 | "@angular/compiler-cli": "^2.3.1", 32 | "@types/jasmine": "2.5.38", 33 | "@types/node": "^6.0.42", 34 | "angular-cli": "1.0.0-beta.24", 35 | "codelyzer": "~2.0.0-beta.1", 36 | "jasmine-core": "2.5.2", 37 | "jasmine-spec-reporter": "2.5.0", 38 | "karma": "1.2.0", 39 | "karma-chrome-launcher": "^2.0.0", 40 | "karma-cli": "^1.0.1", 41 | "karma-jasmine": "^1.0.2", 42 | "karma-remap-istanbul": "^0.2.1", 43 | "protractor": "~4.0.13", 44 | "ts-node": "1.2.1", 45 | "tslint": "^4.0.2", 46 | "typescript": "~2.0.3" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /dist/README.md: -------------------------------------------------------------------------------- 1 | ## Can't find dexie.js? 2 | Dexie's dist files are no longer checked in to github except temporarily when tagging 3 | a release version (just so that bower will continue to work). The reason for this is because 4 | checking in dist files bloats the commit history and makes it error prone to contribute to the 5 | repo. To support bower though, we have to continue checking in dist files when releasing, 6 | but that is now handled by the release.sh script, who also removes them directly afterwards. 7 | 8 | If you still just want to download dexie.js to include in a test HTML page, go 9 | to the following download site: 10 | 11 | ### Download 12 | [dexie.min.js](https://unpkg.com/dexie/dist/dexie.min.js) 13 | 14 | [dexie.min.js.map](https://unpkg.com/dexie/dist/dexie.min.js.map) 15 | 16 | ### Typings 17 | [dexie.d.ts](https://unpkg.com/dexie/dist/dexie.d.ts) 18 | 19 | ### Optional Stuff 20 | [dexie.js (non-minified version)](https://unpkg.com/dexie/dist/dexie.js) 21 | 22 | [dexie.js.map](https://unpkg.com/dexie/dist/dexie.js.map) 23 | 24 | [dexie.min.js.gz (Minified and gzipped)](https://unpkg.com/dexie/dist/dexie.min.js.gz) 25 | 26 | ## Install from NPM 27 | ``` 28 | npm install dexie --save 29 | ``` 30 | 31 | ## Install from bower 32 | ``` 33 | bower install dexie --save 34 | ``` 35 | 36 | ## How to build 37 | 1. cd to dexie package 38 | 2. npm install 39 | 3. npm run build 40 | 41 | ## Contributing to Dexie.js? 42 | 43 | Watch: 44 | ``` 45 | npm run watch 46 | ``` 47 | 48 | Test: 49 | ``` 50 | npm test 51 | ``` 52 | -------------------------------------------------------------------------------- /samples/electron/README.md: -------------------------------------------------------------------------------- 1 | # [Electron is awesome!](http://electron.atom.io) 2 | It enables you to build portable desktop apps like `#slack`, `atom` and `visual studio code`. 3 | 4 | Even more awesome when using the built-in standard indexedDB database via Dexie.js. 5 | 6 | Here is just very simple sample to use Dexie to persist your app data in offline Electron apps. 7 | 8 | ### [main.js](https://github.com/dfahlander/Dexie.js/blob/master/samples/electron/main.js) 9 | 10 | ..is just a cut'n'paste from [electron's get started sample](https://github.com/electron/electron/blob/master/docs/tutorial/quick-start.md#write-your-first-electron-app) 11 | 12 | ### [index.html](https://github.com/dfahlander/Dexie.js/blob/master/samples/electron/index.html) 13 | 14 | ..is (almost) just a cut'n'paste from [Dexie's ES6 sample](https://github.com/dfahlander/Dexie.js#hello-world-es2015--es6). 15 | 16 | Note: This sample sets `Dexie.debug = true;`. Read more about [Dexie.debug](https://github.com/dfahlander/Dexie.js/wiki/Dexie.debug) if you like, and how to automate that in a build step. 17 | 18 | 19 | ### No build steps 20 | 21 | * No need for a transpilation step because Electron's chromium version already understand most of ES6. 22 | * No need for a bundling step because Electron's web pages has native window.require(). 23 | 24 | Like. It. a. Lot! 25 | 26 | ## Usage 27 | ``` 28 | npm install 29 | npm start 30 | ``` 31 | 32 | No prerequisits. Well, you need a computer. And nodejs of course. Electron is installed with the dependencies. 33 | 34 | -------------------------------------------------------------------------------- /samples/typescript/README.md: -------------------------------------------------------------------------------- 1 | # Sample - Using Dexie.js with Typescript 2.1 2 | *NOTE: This sample requires beta versions of Dexie and Typescript. If you prefer stable packages, see [typescript-simple](https://github.com/dfahlander/Dexie.js/tree/master/samples/typescript-simple)* 3 | 4 | This is a sample on how to use Dexie.js with Typescript 2.1. The following features are shown: 5 | 6 | * How to subclass Dexie and define tables in a type-safe manner. 7 | * How to create an entity with Dexie. 8 | * How to use async / await with Dexie. 9 | * How to create something similar to navigation properties on entities. 10 | * Compile directly to ES5 with just typescript 2.1 and rollup (no babel). 11 | 12 | ## Install 13 | ``` 14 | npm install 15 | ``` 16 | 17 | ## Build 18 | ``` 19 | npm run build 20 | ``` 21 | 22 | ## Run 23 | ``` 24 | npm test 25 | ``` 26 | Surf to http://localhost:8081/src/app.html 27 | 28 | # The app 29 | The application stores a simple contact database using a relational database model, where each contact can have 30 | 0..n emails, 0..n phone numbers. Email- and Phone entries have their own tables and reference Contact through 31 | the contactId index. 32 | 33 | The AppDatabase class extends Dexie with the three tables **contacts**, **emails** and **phones**. 34 | The tables are mapped to typescript classes and interfaces. 35 | 36 | The sample shows how to subclass Dexie in typescript and define the tables. 37 | 38 | It also shows how to map a typescript class to a database table and call methods on database objects. 39 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/src/delete-old-changes.js: -------------------------------------------------------------------------------- 1 | import Dexie from 'dexie'; 2 | 3 | export default function deleteOldChanges(db) { 4 | // This is a background job and should never be done within 5 | // a caller's transaction. Use Dexie.ignoreTransaction() to ensure that. 6 | // We should not return the Promise but catch it ourselves instead. 7 | 8 | // To prohibit starving the database we want to lock transactions as short as possible 9 | // and since we're not in a hurry, we could do this job in chunks and reschedule a 10 | // continuation every 500 ms. 11 | const CHUNK_SIZE = 100; 12 | 13 | Dexie.ignoreTransaction(()=>{ 14 | return db._syncNodes.orderBy("myRevision").first(oldestNode => { 15 | return db._changes 16 | .where("rev").below(oldestNode.myRevision) 17 | .limit(CHUNK_SIZE) 18 | .primaryKeys(); 19 | }).then(keysToDelete => { 20 | if (keysToDelete.length === 0) return; // Done. 21 | return db._changes.bulkDelete(keysToDelete).then(()=> { 22 | // If not done garbage collecting, reschedule a continuation of it until done. 23 | if (keysToDelete.length === CHUNK_SIZE) { 24 | // Limit reached. Changes are there are more job to do. Schedule again: 25 | setTimeout(() => db.isOpen() && deleteOldChanges(db), 500); 26 | } 27 | }); 28 | }); 29 | }).catch(()=>{ 30 | // The operation is not crucial. A failure could almost only be due to that database has been closed. 31 | // No need to log this. 32 | }); 33 | } 34 | -------------------------------------------------------------------------------- /src/public/types/where-clause.d.ts: -------------------------------------------------------------------------------- 1 | import { IndexableTypeArray, IndexableTypeArrayReadonly } from "./indexable-type"; 2 | import { Collection } from "./collection"; 3 | import { IndexableType } from "./indexable-type"; 4 | 5 | export interface WhereClause { 6 | above(key: any): Collection; 7 | aboveOrEqual(key: any): Collection; 8 | anyOf(keys: IndexableTypeArrayReadonly): Collection; 9 | anyOf(...keys: IndexableTypeArray): Collection; 10 | anyOfIgnoreCase(keys: string[]): Collection; 11 | anyOfIgnoreCase(...keys: string[]): Collection; 12 | below(key: any): Collection; 13 | belowOrEqual(key: any): Collection; 14 | between(lower: any, upper: any, includeLower?: boolean, includeUpper?: boolean): Collection; 15 | equals(key: any): Collection; 16 | equalsIgnoreCase(key: string): Collection; 17 | inAnyRange(ranges: ReadonlyArray<{0: any, 1: any}>, options?: { includeLowers?: boolean, includeUppers?: boolean }): Collection; 18 | startsWith(key: string): Collection; 19 | startsWithAnyOf(prefixes: string[]): Collection; 20 | startsWithAnyOf(...prefixes: string[]): Collection; 21 | startsWithIgnoreCase(key: string): Collection; 22 | startsWithAnyOfIgnoreCase(prefixes: string[]): Collection; 23 | startsWithAnyOfIgnoreCase(...prefixes: string[]): Collection; 24 | noneOf(keys: Array): Collection; 25 | notEqual(key: any): Collection; 26 | } 27 | -------------------------------------------------------------------------------- /src/dbcore/proxy-cursor.ts: -------------------------------------------------------------------------------- 1 | import { DBCoreCursor, Key } from '../public/types/dbcore'; 2 | 3 | export interface ProxyCursorHooks { 4 | // Methods 5 | getKey?: () => Key; 6 | getPrimaryKey?: () => Key; 7 | getValue?: ( )=> any; 8 | continue?: (key?: Key, primaryKey?: Key) => void; 9 | start?: (onNext: ()=>void) => Promise; 10 | } 11 | 12 | export function ProxyCursor ( 13 | cursor: DBCoreCursor, 14 | fixManualAdvance: boolean, 15 | { 16 | getKey, 17 | getPrimaryKey, 18 | getValue, 19 | continue: doContinue, 20 | start 21 | }: ProxyCursorHooks 22 | ) : DBCoreCursor 23 | { 24 | if (!cursor) return null; 25 | const props: PropertyDescriptorMap = {}; 26 | if (getKey) props.key = {get: getKey}; 27 | if (getPrimaryKey) props.primaryKey = {get: getPrimaryKey}; 28 | if (getValue) props.value = {get: getValue}; 29 | if (doContinue) { 30 | props.continue = props.continuePrimaryKey = {value: doContinue}; 31 | } 32 | if (fixManualAdvance) { 33 | const continueNext = doContinue || cursor.continue; 34 | const doStart = start || cursor.start; 35 | let skip = 0; 36 | props.start = { 37 | value: (onNext: ()=>void) => { 38 | return doStart(() => skip ? (--skip, continueNext()) : onNext()); 39 | } 40 | }; 41 | props.advance = { 42 | value: (count: number) => { 43 | if (count > 1) skip = count; 44 | continueNext(); 45 | } 46 | }; 47 | } else if (start) { 48 | props.start = {value: start}; 49 | } 50 | 51 | return Object.create(cursor, props); 52 | } 53 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/src/apply-changes.js: -------------------------------------------------------------------------------- 1 | import Dexie from 'dexie'; 2 | import { CREATE, DELETE, UPDATE } from './change_types'; 3 | import bulkUpdate from './bulk-update'; 4 | 5 | export default function initApplyChanges(db) { 6 | return function applyChanges(changes) { 7 | let collectedChanges = {}; 8 | changes.forEach((change) => { 9 | if (!collectedChanges.hasOwnProperty(change.table)) { 10 | collectedChanges[change.table] = { [CREATE]: [], [DELETE]: [], [UPDATE]: [] }; 11 | } 12 | collectedChanges[change.table][change.type].push(change); 13 | }); 14 | let table_names = Object.keys(collectedChanges); 15 | let tables = table_names.map((table) => db.table(table)); 16 | 17 | return db.transaction("rw", tables, () => { 18 | table_names.forEach((table_name) => { 19 | const table = db.table(table_name); 20 | const specifyKeys = !table.schema.primKey.keyPath; 21 | const createChangesToApply = collectedChanges[table_name][CREATE]; 22 | const deleteChangesToApply = collectedChanges[table_name][DELETE]; 23 | const updateChangesToApply = collectedChanges[table_name][UPDATE]; 24 | if (createChangesToApply.length > 0) 25 | table.bulkPut(createChangesToApply.map(c => c.obj), specifyKeys ? 26 | createChangesToApply.map(c => c.key) : undefined); 27 | if (updateChangesToApply.length > 0) 28 | bulkUpdate(table, updateChangesToApply); 29 | if (deleteChangesToApply.length > 0) 30 | table.bulkDelete(deleteChangesToApply.map(c => c.key)); 31 | }); 32 | }); 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /tools/travis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # 4 | # eslint 5 | # 6 | printf "Running eslint Dexie src\n" 7 | $(npm bin)/eslint src 8 | printf "eslint ok.\n" 9 | 10 | printf "Running eslint Dexie.Syncable src\n" 11 | $(npm bin)/eslint --config "addons/Dexie.Syncable/src/.eslintrc.json" "addons/Dexie.Syncable/src" 12 | printf "eslint ok.\n\n" 13 | 14 | printf "Running eslint Dexie.Observable src\n" 15 | $(npm bin)/eslint --config "addons/Dexie.Observable/src/.eslintrc.json" "addons/Dexie.Observable/src" 16 | printf "eslint ok.\n\n" 17 | 18 | # 19 | # Build 20 | # 21 | 22 | printf "Building Dexie\n" 23 | npm run build 24 | printf "Dexie building done.\n\n" 25 | 26 | ADDONS_DIR="addons/" 27 | # Use an array to make sure that Observable is built before Syncable 28 | addons=("Dexie.Observable" "Dexie.Syncable") 29 | 30 | # build addons 31 | for addon in "${addons[@]}" 32 | do 33 | dir="${ADDONS_DIR}${addon}" 34 | # Copy Dexie node_modules to avoid having to install them for each addon 35 | cp -R 'node_modules' ${dir} 36 | cd ${dir} 37 | printf "Building ${addon}\n" 38 | npm run build 39 | printf "${addon} building done.\n\n" 40 | cd - 41 | done 42 | 43 | # test 44 | printf "Testing Dexie\n" 45 | $(npm bin)/karma start test/karma.travis.conf.js --single-run 46 | printf "Dexie tests done.\n\n" 47 | 48 | # Run tests for addons 49 | for addon in "${addons[@]}" 50 | do 51 | dir="${ADDONS_DIR}${addon}" 52 | cd ${dir} 53 | printf "Testing ${addon}\n" 54 | $(npm bin)/karma start test/karma.travis.conf.js --single-run 55 | printf "${addon} tests done.\n\n" 56 | cd - 57 | done 58 | 59 | printf "Done.\n" 60 | -------------------------------------------------------------------------------- /src/classes/where-clause/where-clause-constructor.ts: -------------------------------------------------------------------------------- 1 | import { Dexie } from '../dexie'; 2 | import { makeClassConstructor } from '../../functions/make-class-constructor'; 3 | import { WhereClause } from './where-clause'; 4 | import { Table } from '../table'; 5 | import { Collection } from '../collection'; 6 | import { exceptions } from '../../errors'; 7 | 8 | export interface WhereClauseConstructor { 9 | new(table: Table, index?: string, orCollection?: Collection): WhereClause; 10 | prototype: WhereClause; 11 | } 12 | 13 | /** Generates a WhereClause constructor. 14 | * 15 | * The purpose of having dynamically created constructors, is to allow 16 | * addons to extend classes for a certain Dexie instance without affecting 17 | * other db instances. 18 | */ 19 | export function createWhereClauseConstructor(db: Dexie) { 20 | return makeClassConstructor( 21 | WhereClause.prototype, 22 | 23 | function WhereClause(this: WhereClause, table: Table, index?: string, orCollection?: Collection) { 24 | this.db = db; 25 | this._ctx = { 26 | table: table, 27 | index: index === ":id" ? null : index, 28 | or: orCollection 29 | }; 30 | const indexedDB = db._deps.indexedDB; 31 | if (!indexedDB) throw new exceptions.MissingAPI("indexedDB API missing"); 32 | this._cmp = this._ascending = indexedDB.cmp.bind(indexedDB); 33 | this._descending = (a, b) => indexedDB.cmp(b, a); 34 | this._max = (a, b) => indexedDB.cmp(a,b) > 0 ? a : b; 35 | this._min = (a, b) => indexedDB.cmp(a,b) < 0 ? a : b; 36 | this._IDBKeyRange = db._deps.IDBKeyRange; 37 | } 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /addons/dexie-export-import/test/tools.ts: -------------------------------------------------------------------------------- 1 | import {asyncTest, start, stop, ok, equal} from 'qunit'; 2 | 3 | export function promisedTest(name: string, tester: ()=>Promise) { 4 | asyncTest(name, async ()=>{ 5 | try { 6 | await tester(); 7 | } catch (error) { 8 | ok(false, "Got error: " + (error ? 9 | error + 10 | (error.code ? ` (code: ${error.code})` : ``) + 11 | (error.stack ? "\n" + error.stack : '') : 12 | error)); 13 | } finally { 14 | start(); 15 | } 16 | }); 17 | } 18 | 19 | export function readBlob(blob: Blob): Promise { 20 | return new Promise((resolve, reject) => { 21 | const reader = new FileReader(); 22 | reader.onabort = ev => reject(new Error("file read aborted")); 23 | reader.onerror = ev => reject((ev.target as any).error); 24 | reader.onload = ev => resolve((ev.target as any).result); 25 | reader.readAsText(blob); 26 | }); 27 | } 28 | 29 | export function readBlobBinary(blob: Blob): Promise { 30 | return new Promise((resolve, reject) => { 31 | const reader = new FileReader(); 32 | reader.onabort = ev => reject(new Error("file read aborted")); 33 | reader.onerror = ev => reject((ev.target as any).error); 34 | reader.onload = ev => resolve((ev.target as any).result); 35 | reader.readAsArrayBuffer(blob); 36 | }); 37 | } 38 | 39 | // Must use this rather than QUnit's deepEqual() because that one fails on Safari when run via karma-browserstack-launcher 40 | export function deepEqual(a: any, b: any, description: string) { 41 | equal(JSON.stringify(a, null, 2), JSON.stringify(b, null, 2), description); 42 | } 43 | -------------------------------------------------------------------------------- /src/dbcore/lockable-table-middleware.ts: -------------------------------------------------------------------------------- 1 | import { DBCoreTable, DBCoreTransaction } from '../public/types/dbcore'; 2 | 3 | let counter = 0; 4 | 5 | export function LockableTableMiddleware (tableMiddleware: DBCoreTable) : {lock (trans: DBCoreTransaction, p: Promise): Promise, lockableMiddleware: DBCoreTable} { 6 | const lockableMiddleware = {...tableMiddleware}; 7 | const lockerId = ++counter; 8 | //const locks = [] as DBCoreTransaction[]; 9 | //const promises = [] as Promise[]; 10 | Object.keys(tableMiddleware).forEach(m => { 11 | const method = tableMiddleware[m]; 12 | if (typeof method === 'function') { 13 | const guardedMethod = (req: {trans: DBCoreTransaction}) => { 14 | const lockPromise = (req.trans["_lock"+lockerId]); 15 | if (lockPromise) { 16 | // Method is locked for this transaction. 17 | // Wait until its promise resolves. 18 | // Then re-call this guardedMethod to check locks again. 19 | return lockPromise.then(()=>guardedMethod(req)); 20 | } 21 | return method(req); 22 | } 23 | lockableMiddleware[m] = guardedMethod; 24 | } 25 | }); 26 | return { 27 | lock(trans: DBCoreTransaction, p: Promise){ 28 | function unlock() { 29 | delete trans["_lock"+lockerId]; 30 | } 31 | p = p.then(res => { 32 | // TODO: Use finally() instead. 33 | unlock(); 34 | return res; 35 | }).catch(err => { 36 | unlock(); 37 | return Promise.reject(err); 38 | }); 39 | trans["_lock"+lockerId] = p; 40 | return p; 41 | }, 42 | lockableMiddleware 43 | }; 44 | } 45 | -------------------------------------------------------------------------------- /src/classes/table/table-constructor.ts: -------------------------------------------------------------------------------- 1 | import { Dexie } from '../dexie'; 2 | import { TableSchema } from '../../public/types/table-schema'; 3 | import { Transaction } from '../transaction/transaction'; 4 | import { hookCreatingChain, pureFunctionChain, nop, mirror, hookUpdatingChain, hookDeletingChain } from '../../functions/chaining-functions'; 5 | import { TableHooks } from '../../public/types/table-hooks'; 6 | import { Table } from './table'; 7 | import Events from '../../helpers/Events'; 8 | import { makeClassConstructor } from '../../functions/make-class-constructor'; 9 | 10 | export interface TableConstructor { 11 | new (name: string, tableSchema: TableSchema, optionalTrans?: Transaction) : Table; 12 | prototype: Table; 13 | } 14 | 15 | /** Generates a Table constructor bound to given Dexie instance. 16 | * 17 | * The purpose of having dynamically created constructors, is to allow 18 | * addons to extend classes for a certain Dexie instance without affecting 19 | * other db instances. 20 | */ 21 | export function createTableConstructor (db: Dexie) { 22 | return makeClassConstructor( 23 | Table.prototype, 24 | 25 | function Table (this: Table, name: string, tableSchema: TableSchema, trans?: Transaction) { 26 | this.db = db; 27 | this._tx = trans; 28 | this.name = name; 29 | this.schema = tableSchema; 30 | this.hook = db._allTables[name] ? db._allTables[name].hook : Events(null, { 31 | "creating": [hookCreatingChain, nop], 32 | "reading": [pureFunctionChain, mirror], 33 | "updating": [hookUpdatingChain, nop], 34 | "deleting": [hookDeletingChain, nop] 35 | }) as TableHooks; 36 | } 37 | 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/test/integration/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Include common configuration 2 | const {karmaCommon, getKarmaConfig, defaultBrowserMatrix} = require('../../../../test/karma.common'); 3 | 4 | module.exports = function (config) { 5 | const browserMatrixOverrides = { 6 | // Be fine with testing on local travis firefox + browserstack chrome, latest supported. 7 | ci: ["Firefox", "bs_chrome_latest_supported"], 8 | // This addon is not yet ready for full-blown tests on iphone/Safari. That's one of the reason it is still in beta. 9 | // Firefox 55 has bug that is triggered when addons are present. Bug is fixed for FF57: https://bugzilla.mozilla.org/show_bug.cgi?id=1395071 10 | pre_npm_publish: defaultBrowserMatrix.pre_npm_publish.filter(b => 11 | !/bs_iphone7|bs_firefox_latest_supported/i.test(b)) 12 | }; 13 | 14 | const cfg = getKarmaConfig(browserMatrixOverrides, { 15 | // Base path should point at the root 16 | basePath: '../../../../', 17 | // The files needed to apply dexie-observable to the standard dexie unit tests. 18 | files: karmaCommon.files.concat([ 19 | 'dist/dexie.js', 20 | 'addons/Dexie.Syncable/test/integration/karma-env.js', 21 | 'addons/Dexie.Observable/dist/dexie-observable.js', // Apply observable addon 22 | 'addons/Dexie.Syncable/dist/dexie-syncable.js', // Apply syncable addon 23 | 'addons/Dexie.Syncable/test/integration/dummy-sync-protocol.js', 24 | 'test/bundle.js', // The dexie standard test suite 25 | { pattern: 'addons/Dexie.Observable/dist/*.map', watched: false, included: false }, 26 | { pattern: 'addons/Dexie.Syncable/dist/*.map', watched: false, included: false } 27 | ]) 28 | }); 29 | 30 | config.set(cfg); 31 | } 32 | -------------------------------------------------------------------------------- /samples/angular2/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | import { TodoWithID, Todo, TodosService } from './todos.service'; 4 | 5 | @Component({ 6 | selector: 'app-root', 7 | template: `
    8 |

    Angular 2 + Dexie Todo Example

    9 | 10 | 15 |
    `, 16 | }) 17 | export class AppComponent implements OnInit { 18 | todosList: Array = []; 19 | constructor(private todosService: TodosService) {} 20 | 21 | ngOnInit() { 22 | this.todosService.getAll().then((todos: Array) => { 23 | this.todosList = todos; 24 | }); 25 | } 26 | 27 | onAddTodo(title: string) { 28 | const todo: Todo = { 29 | title, 30 | done: false, 31 | }; 32 | this.todosService 33 | .add(todo) 34 | .then((id) => { 35 | this.todosList = [...this.todosList, Object.assign({}, todo, { id })]; 36 | }); 37 | } 38 | 39 | onToggleTodo({ id, done }: { id: number, done: boolean }) { 40 | this.todosService 41 | .update(id, { done }) 42 | .then(() => { 43 | const todoToUpdate = this.todosList.find((todo) => todo.id === id); 44 | this.todosList = [...this.todosList.filter((todo) => todo.id !== id), Object.assign({}, todoToUpdate, { done })]; 45 | }); 46 | } 47 | 48 | onDeleteTodo(id: number) { 49 | this.todosService 50 | .remove(id) 51 | .then(() => { 52 | this.todosList = this.todosList.filter((todo) => todo.id !== id); 53 | }); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/test/unit/tests-override-open.js: -------------------------------------------------------------------------------- 1 | import {module, test, strictEqual, deepEqual, ok} from 'QUnit'; 2 | import initOverrideOpen from '../../src/override-open'; 3 | 4 | module('override-open', { 5 | setup: () => { 6 | }, 7 | teardown: () => { 8 | } 9 | }); 10 | 11 | test('should call the given original function', () => { 12 | let wasCalled = false; 13 | function origFn() { 14 | wasCalled = true; 15 | } 16 | 17 | const db = { 18 | _allTables: {} 19 | }; 20 | 21 | initOverrideOpen(db, function SyncNode() {}, function crudMonitor() {})(origFn)(); 22 | ok(wasCalled); 23 | }); 24 | 25 | test('should call the crudMonitor function for every observable table', () => { 26 | const tables = []; 27 | function crudMonitor(table) { 28 | tables.push(table); 29 | } 30 | 31 | const db = { 32 | _allTables: { 33 | foo: { 34 | // TableSchema: for more info see https://github.com/dfahlander/Dexie.js/wiki/TableSchema 35 | schema: { 36 | observable: true 37 | } 38 | }, 39 | _bar: { 40 | schema: {} 41 | } 42 | } 43 | }; 44 | 45 | initOverrideOpen(db, function SyncNode() {}, crudMonitor)(() => {})(); 46 | deepEqual(tables, [db._allTables.foo]); 47 | }); 48 | 49 | test('should call mapToClass for the _syncNodes table', () => { 50 | function SyncNode() {} 51 | let calledWithClass; 52 | const db = { 53 | _allTables: { 54 | _syncNodes: { 55 | name: '_syncNodes', 56 | mapToClass(cls) { 57 | calledWithClass = cls; 58 | }, 59 | schema: {} 60 | } 61 | } 62 | }; 63 | 64 | initOverrideOpen(db, SyncNode, function crudMonitor(){})(() => {})(); 65 | strictEqual(calledWithClass, SyncNode); 66 | }); 67 | -------------------------------------------------------------------------------- /addons/Dexie.Observable/src/hooks/creating.js: -------------------------------------------------------------------------------- 1 | import Dexie from 'dexie'; 2 | 3 | import {CREATE} from '../change_types'; 4 | import {createUUID} from '../utils'; 5 | 6 | export default function initCreatingHook(db, table) { 7 | return function creatingHook(primKey, obj, trans) { 8 | /// 9 | var rv = undefined; 10 | if (primKey === undefined && table.schema.primKey.uuid) { 11 | primKey = rv = createUUID(); 12 | if (table.schema.primKey.keyPath) { 13 | Dexie.setByKeyPath(obj, table.schema.primKey.keyPath, primKey); 14 | } 15 | } 16 | 17 | var change = { 18 | source: trans.source || null, // If a "source" is marked on the transaction, store it. Useful for observers that want to ignore their own changes. 19 | table: table.name, 20 | key: primKey === undefined ? null : primKey, 21 | type: CREATE, 22 | obj: obj 23 | }; 24 | 25 | var promise = db._changes.add(change).then(function (rev) { 26 | trans._lastWrittenRevision = Math.max(trans._lastWrittenRevision, rev); 27 | return rev; 28 | }); 29 | 30 | // Wait for onsuccess so that we have the primKey if it is auto-incremented and update the change item if so. 31 | this.onsuccess = function (resultKey) { 32 | if (primKey != resultKey) 33 | promise._then(function () { 34 | change.key = resultKey; 35 | db._changes.put(change); 36 | }); 37 | }; 38 | 39 | this.onerror = function () { 40 | // If the main operation fails, make sure to regret the change 41 | promise._then(function (rev) { 42 | // Will only happen if app code catches the main operation error to prohibit transaction from aborting. 43 | db._changes.delete(rev); 44 | }); 45 | }; 46 | 47 | return rv; 48 | }; 49 | } 50 | -------------------------------------------------------------------------------- /addons/dexie-export-import/tools/build-configs/rollup.config.js: -------------------------------------------------------------------------------- 1 | import sourcemaps from 'rollup-plugin-sourcemaps'; 2 | import {readFileSync} from 'fs'; 3 | import path from 'path'; 4 | import commonjs from 'rollup-plugin-commonjs'; 5 | import nodeResolve from 'rollup-plugin-node-resolve'; 6 | 7 | const version = require(path.resolve(__dirname, '../../package.json')).version; 8 | 9 | const ERRORS_TO_IGNORE = [ 10 | "THIS_IS_UNDEFINED", 11 | "UNRESOLVED_IMPORT", // 'stream' is imported by clarinet 12 | "MISSING_GLOBAL_NAME", // global name "stream" (also clarinet) 13 | "MISSING_NODE_BUILTINS" // "stream" (also clarinet) 14 | ]; 15 | 16 | export default { 17 | input: 'tools/tmp/src/dexie-export-import.js', 18 | output: [{ 19 | file: 'dist/dexie-export-import.js', 20 | format: 'umd', 21 | banner: readFileSync(path.resolve(__dirname, 'banner.txt'))+"" 22 | .replace(/{version}/g, version) 23 | .replace(/{date}/g, new Date().toDateString()), 24 | globals: {dexie: "Dexie"}, 25 | name: 'DexieExportImport', 26 | sourcemap: true, 27 | exports: 'named' 28 | },{ 29 | file: 'dist/dexie-export-import.mjs', 30 | format: 'es', 31 | banner: readFileSync(path.resolve(__dirname, 'banner.txt'))+"" 32 | .replace(/{version}/g, version) 33 | .replace(/{date}/g, new Date().toDateString()), 34 | sourcemap: true 35 | }], 36 | external: ['dexie'], 37 | plugins: [ 38 | sourcemaps(), 39 | nodeResolve({browser: true}), 40 | commonjs() 41 | ], 42 | onwarn ({loc, frame, code, message}) { 43 | if (ERRORS_TO_IGNORE.includes(code)) return; 44 | if ( loc ) { 45 | console.warn( `${loc.file} (${loc.line}:${loc.column}) ${message}` ); 46 | if ( frame ) console.warn( frame ); 47 | } else { 48 | console.warn(`${code} ${message}`); 49 | } 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /src/classes/dexie/generate-middleware-stacks.ts: -------------------------------------------------------------------------------- 1 | import { Dexie } from './'; 2 | import { createDBCore } from '../../dbcore/dbcore-indexeddb'; 3 | import { DBCore } from '../../public/types/dbcore'; 4 | import { DexieDOMDependencies } from '../../public/types/dexie-dom-dependencies'; 5 | import { DexieStacks, Middleware } from '../../public/types/middleware'; 6 | import { exceptions } from '../../errors'; 7 | 8 | function createMiddlewareStack( 9 | stackImpl: {stack: string}, 10 | middlewares: Middleware<{stack: string}>[]): TStack { 11 | return middlewares.reduce((down, {create}) => ({...down, ...create(down)}), stackImpl) as TStack; 12 | } 13 | 14 | function createMiddlewareStacks( 15 | middlewares: {[StackName in keyof DexieStacks]?: Middleware[]}, 16 | idbdb: IDBDatabase, 17 | {IDBKeyRange, indexedDB}: DexieDOMDependencies, 18 | tmpTrans: IDBTransaction): {[StackName in keyof DexieStacks]?: DexieStacks[StackName]} 19 | { 20 | const dbcore = createMiddlewareStack( 21 | createDBCore(idbdb, indexedDB, IDBKeyRange, tmpTrans), 22 | middlewares.dbcore); 23 | 24 | // TODO: Create other stacks the same way as above. They might be dependant on the result 25 | // of creating dbcore stack. 26 | 27 | return { 28 | dbcore 29 | }; 30 | } 31 | 32 | export function generateMiddlewareStacks(db: Dexie, tmpTrans: IDBTransaction) { 33 | const idbdb = tmpTrans.db; 34 | const stacks = createMiddlewareStacks(db._middlewares, idbdb, db._deps, tmpTrans); 35 | db.core = stacks.dbcore!; 36 | db.tables.forEach(table => { 37 | const tableName = table.name; 38 | if (db.core.schema.tables.some(tbl => tbl.name === tableName)) { 39 | table.core = db.core.table(tableName); 40 | if (db[tableName] instanceof db.Table) { 41 | db[tableName].core = table.core; 42 | } 43 | } 44 | }); 45 | } 46 | -------------------------------------------------------------------------------- /samples/electron/main.js: -------------------------------------------------------------------------------- 1 | const electron = require('electron'); 2 | // Module to control application life. 3 | const {app} = electron; 4 | // Module to create native browser window. 5 | const {BrowserWindow} = electron; 6 | 7 | // Keep a global reference of the window object, if you don't, the window will 8 | // be closed automatically when the JavaScript object is garbage collected. 9 | let win; 10 | 11 | function createWindow() { 12 | // Create the browser window. 13 | win = new BrowserWindow({width: 800, height: 600}); 14 | 15 | // and load the index.html of the app. 16 | win.loadURL(`file://${__dirname}/index.html`); 17 | 18 | // Open the DevTools. 19 | win.webContents.openDevTools(); 20 | 21 | // Emitted when the window is closed. 22 | win.on('closed', () => { 23 | // Dereference the window object, usually you would store windows 24 | // in an array if your app supports multi windows, this is the time 25 | // when you should delete the corresponding element. 26 | win = null; 27 | }); 28 | } 29 | 30 | // This method will be called when Electron has finished 31 | // initialization and is ready to create browser windows. 32 | // Some APIs can only be used after this event occurs. 33 | app.on('ready', createWindow); 34 | 35 | // Quit when all windows are closed. 36 | app.on('window-all-closed', () => { 37 | // On OS X it is common for applications and their menu bar 38 | // to stay active until the user quits explicitly with Cmd + Q 39 | if (process.platform !== 'darwin') { 40 | app.quit(); 41 | } 42 | }); 43 | 44 | app.on('activate', () => { 45 | // On OS X it's common to re-create a window in the app when the 46 | // dock icon is clicked and there are no other windows open. 47 | if (win === null) { 48 | createWindow(); 49 | } 50 | }); 51 | 52 | // In this file you can include the rest of your app's specific main process 53 | // code. You can also put them in separate files and require them here. -------------------------------------------------------------------------------- /addons/dexie-export-import/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dexie-export-import", 3 | "version": "1.0.0-alpha.9", 4 | "description": "Dexie addon that adds export and import capabilities", 5 | "main": "dist/dexie-export-import.js", 6 | "module": "dist/dexie-export-import.mjs", 7 | "typings": "dist/dexie-export-import.d.ts", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/dfahlander/Dexie.js.git" 11 | }, 12 | "scripts": { 13 | "test": "just-build test && npx karma start test/karma.conf.js --single-run", 14 | "build": "just-build", 15 | "watch": "just-build --watch", 16 | "clean": "rm -rf tools/tmp dist/*.js dist/*.mjs dist/*.map dist/*.d.ts test/bundle.*" 17 | }, 18 | "just-build": { 19 | "default": [ 20 | "just-build src", 21 | "just-build test" 22 | ], 23 | "src": [ 24 | "tsc -p src", 25 | "rollup -c tools/build-configs/rollup.config.js" 26 | ], 27 | "test": [ 28 | "tsc -p test [--watch 'Watching for file changes.']", 29 | "rollup -c tools/build-configs/rollup.config.js", 30 | "rollup -c tools/build-configs/rollup.tests.config.js" 31 | ] 32 | }, 33 | "author": "david.fahlander@gmail.com", 34 | "license": "Apache-2.0", 35 | "devDependencies": { 36 | "base64-arraybuffer-es6": "*", 37 | "clarinet": "^0.12.1", 38 | "just-build": "^0.9.16", 39 | "karma": "^3.1.1", 40 | "karma-browserstack-launcher": "git+https://github.com/karma-runner/karma-browserstack-launcher.git", 41 | "karma-chrome-launcher": "^2.0.0", 42 | "karma-firefox-launcher": "^1.1.0", 43 | "karma-mocha-reporter": "^2.2.5", 44 | "karma-qunit": "^1.2.1", 45 | "rollup": "^0.66.6", 46 | "rollup-plugin-commonjs": "^9.2.0", 47 | "rollup-plugin-node-resolve": "^3.4.0", 48 | "typescript": "^3.1.3", 49 | "typeson": "^5.8.2", 50 | "typeson-registry": "^1.0.0-alpha.21" 51 | }, 52 | "dependencies": { 53 | "dexie": "^3.0.0-alpha.5 || ^2.0.4" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/src/merge-change.js: -------------------------------------------------------------------------------- 1 | import { CREATE, UPDATE, DELETE } from './change_types'; 2 | import combineCreateAndUpdate from './combine-create-and-update.js'; 3 | import combineUpdateAndUpdate from './combine-update-and-update.js'; 4 | 5 | export default function mergeChange(prevChange, nextChange) { 6 | switch (prevChange.type) { 7 | case CREATE: 8 | switch (nextChange.type) { 9 | case CREATE: 10 | return nextChange; // Another CREATE replaces previous CREATE. 11 | case UPDATE: 12 | return combineCreateAndUpdate(prevChange, nextChange); // Apply nextChange.mods into prevChange.obj 13 | case DELETE: 14 | return nextChange; // Object created and then deleted. If it wasnt for that we MUST handle resent changes, we would skip entire change here. But what if the CREATE was sent earlier, and then CREATE/DELETE at later stage? It would become a ghost object in DB. Therefore, we MUST keep the delete change! If object doesnt exist, it wont harm! 15 | } 16 | break; 17 | case UPDATE: 18 | switch (nextChange.type) { 19 | case CREATE: 20 | return nextChange; // Another CREATE replaces previous update. 21 | case UPDATE: 22 | return combineUpdateAndUpdate(prevChange, nextChange); // Add the additional modifications to existing modification set. 23 | case DELETE: 24 | return nextChange; // Only send the delete change. What was updated earlier is no longer of interest. 25 | } 26 | break; 27 | case DELETE: 28 | switch (nextChange.type) { 29 | case CREATE: 30 | return nextChange; // A resurection occurred. Only create change is of interest. 31 | case UPDATE: 32 | return prevChange; // Nothing to do. We cannot update an object that doesnt exist. Leave the delete change there. 33 | case DELETE: 34 | return prevChange; // Still a delete change. Leave as is. 35 | } 36 | break; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /addons/Dexie.Syncable/test/unit/tests-PersistedContext.js: -------------------------------------------------------------------------------- 1 | import Dexie from 'dexie'; 2 | import observable from 'dexie-observable'; 3 | // Add this so we have the SyncNode.prototype.save method 4 | import syncable from '../../src/Dexie.Syncable'; 5 | import {module, asyncTest, test, start, stop, propEqual, deepEqual, ok} from 'QUnit'; 6 | import {resetDatabase} from '../../../../test/dexie-unittest-utils'; 7 | import initPersistedContext from '../../src/PersistedContext'; 8 | 9 | const db = new Dexie('TestDBTable', {addons: [observable, syncable]}); 10 | db.version(1).stores({foo: '++id'}); 11 | 12 | module('PersistedContext', { 13 | setup: () => { 14 | stop(); 15 | resetDatabase(db).catch(function (e) { 16 | ok(false, "Error resetting database: " + e.stack); 17 | }).finally(start); 18 | }, 19 | teardown: () => { 20 | } 21 | }); 22 | 23 | asyncTest('should save any properties we add to the context into the DB', () => { 24 | const syncNode = new db.observable.SyncNode(); 25 | const PersistedContext = initPersistedContext(syncNode); 26 | let addedNodeID; 27 | db._syncNodes.add(syncNode) 28 | .then((nodeID) => { 29 | addedNodeID = nodeID; 30 | const persistedContext = new PersistedContext(syncNode.id); 31 | syncNode.syncContext = persistedContext; 32 | persistedContext.foobar = 'foobar'; 33 | return persistedContext.save() 34 | }) 35 | .then(() => { 36 | return db._syncNodes.get(addedNodeID); 37 | }) 38 | .then((node) => { 39 | deepEqual(node.syncContext, {foobar: 'foobar', nodeID: addedNodeID}); 40 | }) 41 | .catch(function(err) { 42 | ok(false, "Error: " + err); 43 | }) 44 | .finally(start); 45 | }); 46 | 47 | test('should extend the instance with the given options object', () => { 48 | const syncNode = new db.observable.SyncNode(); 49 | const PersistedContext = initPersistedContext(syncNode); 50 | const persistedContext = new PersistedContext(1, {foo: 'bar'}); 51 | propEqual(persistedContext, {nodeID: 1, foo: 'bar'}); 52 | }); 53 | -------------------------------------------------------------------------------- /test/karma.browserstack.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | browserStack: { 3 | username: process.env.BROWSER_STACK_USERNAME, 4 | accessKey: process.env.BROWSER_STACK_ACCESS_KEY 5 | }, 6 | 7 | customLaunchers: { 8 | bs_firefox_latest_supported: { 9 | base: 'BrowserStack', 10 | browser: 'firefox', 11 | browser_version: '59', 12 | os: 'Windows', 13 | os_version: 7 14 | }, 15 | bs_firefox_oldest_supported: { 16 | base: 'BrowserStack', 17 | browser: 'firefox', 18 | browser_version: '47.0', 19 | os: 'OS X', 20 | os_version: 'El Capitan' 21 | }, 22 | bs_edge_oldest_supported: { 23 | base: 'BrowserStack', 24 | browser: "Edge", 25 | browser_version: '13', 26 | os: 'Windows', 27 | os_version: '10' 28 | }, 29 | bs_edge_latest_supported: { 30 | base: 'BrowserStack', 31 | browser: 'Edge', 32 | browser_version: '17', 33 | os: 'Windows', 34 | os_version: '10' 35 | }, 36 | bs_ie11: { 37 | base: 'BrowserStack', 38 | browser: 'ie', 39 | browser_version: '11', 40 | os: 'Windows', 41 | os_version: 10 42 | }, 43 | bs_chrome_oldest_supported: { 44 | base: 'BrowserStack', 45 | browser: "Chrome", 46 | browser_version: "49", 47 | os: 'OS X', 48 | os_version: 'Mountain Lion' 49 | }, 50 | bs_chrome_latest_supported: { 51 | base: 'BrowserStack', 52 | browser: "Chrome", 53 | browser_version: "66", 54 | os: 'Windows', 55 | os_version: 10 56 | }, 57 | bs_safari_oldest_supported: { 58 | base: 'BrowserStack', 59 | browser: "Safari", 60 | browser_version: "10.1", 61 | os: 'OS X', 62 | os_version: 'Sierra' 63 | }, 64 | bs_safari_latest_supported: { 65 | base: 'BrowserStack', 66 | browser: "Safari", 67 | browser_version: "12", 68 | os: 'OS X', 69 | os_version: 'Mojave' 70 | }, 71 | bs_iphone7: { 72 | base: 'BrowserStack', 73 | browser: "Safari", 74 | browser_version: "10.1", 75 | os: 'iOS', 76 | os_version: "10.3" 77 | } 78 | } 79 | } 80 | --------------------------------------------------------------------------------