@medyll/idae-idbql 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # create-svelte
2
+
3
+ Everything you need to build a Svelte library, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte).
4
+
5
+ Read more about creating a library [in the docs](https://kit.svelte.dev/docs/packaging).
6
+
7
+ ## Creating a project
8
+
9
+ If you're seeing this, you've probably already done this step. Congrats!
10
+
11
+ ```bash
12
+ # create a new project in the current directory
13
+ npm create svelte@latest
14
+
15
+ # create a new project in my-app
16
+ npm create svelte@latest my-app
17
+ ```
18
+
19
+ ## Developing
20
+
21
+ Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
22
+
23
+ ```bash
24
+ npm run dev
25
+
26
+ # or start the server and open the app in a new browser tab
27
+ npm run dev -- --open
28
+ ```
29
+
30
+ Everything inside `src/lib` is part of your library, everything inside `src/routes` can be used as a showcase or preview app.
31
+
32
+ ## Building
33
+
34
+ To build your library:
35
+
36
+ ```bash
37
+ npm run package
38
+ ```
39
+
40
+ To create a production version of your showcase app:
41
+
42
+ ```bash
43
+ npm run build
44
+ ```
45
+
46
+ You can preview the production build with `npm run preview`.
47
+
48
+ > To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
49
+
50
+ ## Publishing
51
+
52
+ Go into the `package.json` and give your package the desired name through the `"name"` option. Also consider adding a `"license"` field and point it to a `LICENSE` file which you can create from a template (one popular option is the [MIT license](https://opensource.org/license/mit/)).
53
+
54
+ To publish your library to [npm](https://www.npmjs.com):
55
+
56
+ ```bash
57
+ npm publish
58
+ ```
@@ -0,0 +1,34 @@
1
+ import { type Where, type ResultsetOptions, type ResultSet } from "@medyll/idae-query";
2
+ export declare class CollectionCore<T = any> {
3
+ protected _store: string;
4
+ private version?;
5
+ private dbName;
6
+ private dBOpenRequest;
7
+ private keyPath;
8
+ constructor(store: string, keyPath: string, args: {
9
+ dbName: string;
10
+ version?: number;
11
+ });
12
+ get name(): string;
13
+ private getDatabase;
14
+ /** get the collection */
15
+ private getCollection;
16
+ /**
17
+ * Retrieves the data from the collection based on the provided query.
18
+ * @param qy - The query object specifying the conditions for filtering the data.
19
+ * @param options - The options object specifying additional operations to be applied on the result set.
20
+ * @returns A promise that resolves to the filtered result set.
21
+ * @throws If an error occurs while retrieving the data.
22
+ */
23
+ where(qy: Where<T>, options?: ResultsetOptions): Promise<ResultSet<T>>;
24
+ get(value: any): Promise<T>;
25
+ getAll(): Promise<T[]>;
26
+ update(keyPathValue: string | number, data: Partial<T>): Promise<unknown>;
27
+ updateWhere(where: Where<T>, data: Partial<T>): Promise<unknown>;
28
+ put(value: Partial<T>): Promise<unknown>;
29
+ /** ok add data to the store */
30
+ add(data: T): Promise<T | boolean>;
31
+ delete(keyPathValue: string | number): Promise<boolean>;
32
+ deleteWhere(where: Where<T>): Promise<boolean>;
33
+ }
34
+ export declare const Collection: CollectionCore;
@@ -0,0 +1,206 @@
1
+ /* path: src\lib\scripts\collection\collection.ts */
2
+ import { Query, getResultset } from "@medyll/idae-query";
3
+ import {} from "@medyll/idae-query";
4
+ import { idbqlEvent } from "../state/idbqlEvent.svelte.js";
5
+ export class CollectionCore {
6
+ _store;
7
+ version;
8
+ dbName;
9
+ dBOpenRequest;
10
+ keyPath;
11
+ constructor(store, keyPath, args) {
12
+ this._store = store;
13
+ this.version = args.version;
14
+ this.dbName = args.dbName;
15
+ this.keyPath = keyPath.split(",")[0].replace(/[\s+]/g, "").replace(/&/, "");
16
+ }
17
+ get name() {
18
+ return this._store;
19
+ }
20
+ async getDatabase() {
21
+ return new Promise((resolve, reject) => {
22
+ const request = indexedDB.open(this.dbName, this.version);
23
+ request.onsuccess = () => resolve(request.result);
24
+ request.onerror = () => reject(request.error);
25
+ });
26
+ }
27
+ /** get the collection */
28
+ async getCollection() {
29
+ const db = await this.getDatabase();
30
+ if (!db.objectStoreNames.contains(this._store)) {
31
+ throw new Error(`Collection ${this._store} not found`);
32
+ }
33
+ return db.transaction(this._store, "readwrite").objectStore(this._store);
34
+ }
35
+ /**
36
+ * Retrieves the data from the collection based on the provided query.
37
+ * @param qy - The query object specifying the conditions for filtering the data.
38
+ * @param options - The options object specifying additional operations to be applied on the result set.
39
+ * @returns A promise that resolves to the filtered result set.
40
+ * @throws If an error occurs while retrieving the data.
41
+ */
42
+ async where(qy, options) {
43
+ const data = await this.getAll();
44
+ const query = new Query(data);
45
+ const resultSet = getResultset(query.where(qy));
46
+ if (options) {
47
+ resultSet.setOptions(options);
48
+ }
49
+ return resultSet;
50
+ }
51
+ get(value) {
52
+ return new Promise(async (resolve, reject) => {
53
+ const storeObj = await this.getCollection();
54
+ const get = storeObj.get(value);
55
+ get.onsuccess = () => resolve(get.result);
56
+ get.onerror = () => reject("not found");
57
+ });
58
+ }
59
+ getAll() {
60
+ return new Promise(async (resolve, reject) => {
61
+ const storeObj = await this.getCollection();
62
+ const getAll = storeObj.getAll();
63
+ getAll.onsuccess = function () {
64
+ resolve(getAll.result);
65
+ };
66
+ getAll.onerror = function () {
67
+ reject("not found");
68
+ };
69
+ });
70
+ }
71
+ async update(keyPathValue, data) {
72
+ const dta = await this.get(keyPathValue);
73
+ return this.put({
74
+ [this.keyPath]: keyPathValue,
75
+ ...dta,
76
+ ...data,
77
+ });
78
+ }
79
+ async updateWhere(where, data) {
80
+ return this.where(where).then((rs) => {
81
+ return new Promise(async (resolve, reject) => {
82
+ Promise.all(rs.map((dta) => {
83
+ if (this.keyPath && dta[this.keyPath]) {
84
+ const newData = {
85
+ [this.keyPath]: dta[this.keyPath],
86
+ ...dta,
87
+ ...data,
88
+ };
89
+ return this.put(newData);
90
+ }
91
+ }))
92
+ .then(() => {
93
+ resolve(true);
94
+ })
95
+ .catch((e) => {
96
+ reject(e);
97
+ });
98
+ });
99
+ });
100
+ }
101
+ // put data to indexedDB, replace collection content if present
102
+ async put(value) {
103
+ return new Promise(async (resolve, reject) => {
104
+ const storeObj = await this.getCollection();
105
+ const put = storeObj.put(value);
106
+ put.onsuccess = async (event) => {
107
+ const updatedData = await this.get(event.target.result);
108
+ resolve(updatedData);
109
+ };
110
+ put.onerror = function () {
111
+ reject("data not put");
112
+ };
113
+ });
114
+ }
115
+ /** ok add data to the store */
116
+ async add(data) {
117
+ return new Promise(async (resolve, reject) => {
118
+ const storeObj = await this.getCollection();
119
+ const add = storeObj.add(data);
120
+ add.onsuccess = async (event) => {
121
+ const updatedData = await this.get(event.target.result);
122
+ resolve(updatedData);
123
+ };
124
+ add.onerror = function (e) {
125
+ console.log(e);
126
+ reject(e);
127
+ };
128
+ });
129
+ }
130
+ async delete(keyPathValue) {
131
+ return new Promise(async (resolve, reject) => {
132
+ const storeObj = await this.getCollection();
133
+ let objectStoreRequest = storeObj.delete(keyPathValue);
134
+ objectStoreRequest.onsuccess = () => {
135
+ idbqlEvent.registerEvent("delete", {
136
+ collection: this._store,
137
+ data: keyPathValue,
138
+ keyPath: this.keyPath,
139
+ });
140
+ resolve(true);
141
+ };
142
+ objectStoreRequest.onerror = function () {
143
+ reject(false);
144
+ };
145
+ });
146
+ }
147
+ async deleteWhere(where) {
148
+ return this.where(where).then((data) => {
149
+ return new Promise(async (resolve, reject) => {
150
+ Promise.all(data.map((data) => {
151
+ if (this.keyPath && data[this.keyPath]) {
152
+ return this.delete(data[this.keyPath]);
153
+ }
154
+ }))
155
+ .then(() => {
156
+ resolve(true);
157
+ })
158
+ .catch((e) => {
159
+ reject(e);
160
+ });
161
+ });
162
+ });
163
+ }
164
+ }
165
+ export const Collection = createIDBStoreProxy(CollectionCore);
166
+ function createIDBStoreProxy(store) {
167
+ return new Proxy(store, {
168
+ construct(target, args) {
169
+ const instance = new target(...args);
170
+ return new Proxy(instance, {
171
+ get(target, prop, receiver) {
172
+ const origMethod = target[prop];
173
+ if (typeof origMethod === "function" &&
174
+ [
175
+ "update",
176
+ "updateWhere",
177
+ "put",
178
+ "add",
179
+ "delete",
180
+ "deleteWhere",
181
+ ].includes(String(prop))) {
182
+ return function (...args) {
183
+ return new Promise(async (resolve, reject) => {
184
+ origMethod
185
+ .apply(instance, args)
186
+ .then((res) => {
187
+ idbqlEvent.registerEvent(prop, {
188
+ collection: instance._store,
189
+ data: res,
190
+ keyPath: instance.keyPath,
191
+ });
192
+ resolve(res);
193
+ })
194
+ .catch((e) => {
195
+ reject(e);
196
+ });
197
+ }).finally(() => { });
198
+ // return origMethod.apply(this, args);
199
+ };
200
+ }
201
+ return Reflect.get(target, prop, receiver);
202
+ },
203
+ });
204
+ },
205
+ });
206
+ }
@@ -0,0 +1,126 @@
1
+ import { CollectionCore } from "../collection/collection.js";
2
+ import { type StateCollectionDyn } from "../state/idbstate.svelte.js";
3
+ export declare enum enumPrimitive {
4
+ id = "id",
5
+ any = "any",
6
+ date = "date",
7
+ text = "text",
8
+ number = "number",// IdbqModelCollectionTemplate
9
+ boolean = "boolean",
10
+ datetime = "datetime",
11
+ url = "url",
12
+ email = "email",
13
+ phone = "phone",
14
+ time = "time",
15
+ password = "password"
16
+ }
17
+ export declare enum TplProperties {
18
+ private = "private",
19
+ readonly = "readonly",
20
+ required = "required"
21
+ }
22
+ type CombineElements<T extends string, U extends string = T> = T extends any ? T | `${T} ${CombineElements<Exclude<U, T>>}` : never;
23
+ type CombinedArgs = CombineElements<TplProperties>;
24
+ type IdbObjectify<T extends string> = `array-of-${T}` | `object-${T}`;
25
+ export type TplCollectionFields = Record<string, string>;
26
+ export type TplFieldPrimitive<T = {}> = keyof typeof enumPrimitive | `text-${"tiny" | "short" | "medium" | "long" | "area"}` | `${string}.${string}` | `fk-${string}.${string}`;
27
+ export type TplObjectFieldPrimitive = IdbObjectify<TplFieldPrimitive>;
28
+ export type TplFieldFk = `fk-${string}.${string}`;
29
+ export type TplFkObject = IdbObjectify<TplFieldFk>;
30
+ export type TplTypes = TplFieldPrimitive | TplObjectFieldPrimitive | TplFieldFk | TplFkObject;
31
+ export type TplFieldArgs = `${TplTypes} (${CombinedArgs})`;
32
+ /** rules */
33
+ export type TplFieldRules = TplFieldArgs | TplTypes;
34
+ export type TplFieldType = TplFieldArgs | TplTypes;
35
+ export type IDbForge = {
36
+ collection?: TplCollectionName;
37
+ fieldName?: keyof TplFields;
38
+ fieldType?: TplFieldType;
39
+ fieldRule?: TplFieldRules;
40
+ fieldArgs?: [keyof typeof TplProperties] | undefined;
41
+ is: any;
42
+ };
43
+ export type IdbqModel<T = Record<string, Record<string, any>>> = {
44
+ readonly [K in keyof T]: CollectionModel<T[K]>;
45
+ };
46
+ export type TplCollectionName<T = TplCollectionFields> = keyof IdbqModel<T>;
47
+ export type Tpl<T = TplCollectionFields> = CollectionModel<T>["template"];
48
+ export type TplFields<T = TplCollectionFields> = CollectionModel<T>["template"]["fields"];
49
+ export type CollectionModel<T = TplCollectionFields> = {
50
+ keyPath: string | any;
51
+ /** @deprecated use ts instead */
52
+ model: any;
53
+ ts: any;
54
+ template: {
55
+ index: string;
56
+ presentation: CombineElements<keyof CollectionModel<T>["ts"]>;
57
+ fields: {
58
+ [K in keyof T]: TplFieldRules;
59
+ };
60
+ fks: {
61
+ [K in TplCollectionName]?: {
62
+ code: K;
63
+ multiple: boolean;
64
+ rules: CombinedArgs;
65
+ };
66
+ };
67
+ };
68
+ };
69
+ type ReadonlyCollections<T extends IdbqModel> = {
70
+ [K in keyof T]: CollectionCore<T[K]["ts"]>;
71
+ };
72
+ type StateCollections<T extends IdbqModel> = {
73
+ [K in keyof T]: StateCollectionDyn<T[K]["ts"]>;
74
+ };
75
+ /**
76
+ * Represents the IndexedDB wrapper for managing database operations.
77
+ * @template T - The type of data stored in the IndexedDB.
78
+ */
79
+ export declare class IdbqlIndexedCore<T = any> {
80
+ #private;
81
+ private databaseName;
82
+ dbVersion: number;
83
+ idbDatabase: IDBDatabase;
84
+ /**
85
+ * Creates an instance of Idbq.
86
+ * @param {string} databaseName - The name of the database.
87
+ */
88
+ constructor(databaseName: string, idbqModel: IdbqModel, version: number);
89
+ get schema(): Record<string, any>;
90
+ get idbqModel(): IdbqModel<Record<string, Record<string, any>>>;
91
+ /**
92
+ * Sets the version of the database.
93
+ * @param {number} version - The version number of the database.
94
+ * @returns {object} - An object with a `stores` method to define the object stores.
95
+ */
96
+ stores(args: Record<string, string>): Promise<unknown>;
97
+ transaction<R>(storeNames: string | string[], mode: IDBTransactionMode, callback: (tx: IDBTransaction) => Promise<R>): Promise<R>;
98
+ private createCollections;
99
+ }
100
+ /**
101
+ * Creates an IndexedDB database store schema for the given model and version.
102
+ *
103
+ * @param model - The IdbqModel representing the structure of the store.
104
+ * @param version - The version number of the store.
105
+ * @returns A function that creates a readonly collection for the given name.
106
+ * The returned function returns an instance of ReadonlyCollections<T> & typeof idb_.
107
+ *
108
+ * @typeparam T - The type of data stored in the collections.
109
+ */
110
+ export declare const createIdbqDb: <T extends IdbqModel>(model: T, version: number) => {
111
+ create: (name: string) => {
112
+ idbDatabase: IdbqlIndexedCore<T>;
113
+ idbql: ReadonlyCollections<T>;
114
+ idbqlState: StateCollections<T>;
115
+ idbqModel: T;
116
+ };
117
+ };
118
+ export declare const idbqBase: <T extends IdbqModel>(model: T, version: number) => {
119
+ create: (name: string) => {
120
+ idbDatabase: IdbqlIndexedCore<T>;
121
+ idbql: ReadonlyCollections<T>;
122
+ idbqlState: StateCollections<T>;
123
+ idbqModel: T;
124
+ };
125
+ };
126
+ export {};
@@ -0,0 +1,156 @@
1
+ /* src\lib\scripts\idbqlCore\idbqlCore.ts */
2
+ import { Collection, CollectionCore } from "../collection/collection.js";
3
+ import { createIdbqlState, } from "../state/idbstate.svelte.js";
4
+ import { Schema } from "./idbqlSchema.js";
5
+ export var enumPrimitive;
6
+ (function (enumPrimitive) {
7
+ enumPrimitive["id"] = "id";
8
+ enumPrimitive["any"] = "any";
9
+ enumPrimitive["date"] = "date";
10
+ enumPrimitive["text"] = "text";
11
+ enumPrimitive["number"] = "number";
12
+ enumPrimitive["boolean"] = "boolean";
13
+ enumPrimitive["datetime"] = "datetime";
14
+ enumPrimitive["url"] = "url";
15
+ enumPrimitive["email"] = "email";
16
+ enumPrimitive["phone"] = "phone";
17
+ enumPrimitive["time"] = "time";
18
+ enumPrimitive["password"] = "password";
19
+ })(enumPrimitive || (enumPrimitive = {}));
20
+ export var TplProperties;
21
+ (function (TplProperties) {
22
+ TplProperties["private"] = "private";
23
+ TplProperties["readonly"] = "readonly";
24
+ TplProperties["required"] = "required";
25
+ })(TplProperties || (TplProperties = {}));
26
+ const a = "object-any (readonly private)";
27
+ /**
28
+ * Represents the IndexedDB wrapper for managing database operations.
29
+ * @template T - The type of data stored in the IndexedDB.
30
+ */
31
+ export class IdbqlIndexedCore {
32
+ databaseName;
33
+ dbVersion;
34
+ idbDatabase;
35
+ #schema = {};
36
+ #idbqModel = {};
37
+ /**
38
+ * Creates an instance of Idbq.
39
+ * @param {string} databaseName - The name of the database.
40
+ */
41
+ constructor(databaseName, idbqModel, version) {
42
+ this.databaseName = databaseName;
43
+ this.#idbqModel = idbqModel;
44
+ this.dbVersion = version;
45
+ const stores = {};
46
+ Object.keys(idbqModel).forEach((modelName) => {
47
+ const modelInfo = idbqModel[modelName];
48
+ stores[modelName] = modelInfo.keyPath || "";
49
+ Object.defineProperty(this, modelName, {
50
+ // @ts-ignore
51
+ value: undefined,
52
+ writable: true,
53
+ enumerable: true,
54
+ configurable: true,
55
+ });
56
+ });
57
+ this.stores(stores);
58
+ }
59
+ get schema() {
60
+ return this.#schema;
61
+ }
62
+ get idbqModel() {
63
+ return this.#idbqModel;
64
+ }
65
+ /**
66
+ * Sets the version of the database.
67
+ * @param {number} version - The version number of the database.
68
+ * @returns {object} - An object with a `stores` method to define the object stores.
69
+ */
70
+ async stores(args) {
71
+ if (typeof indexedDB !== "undefined") {
72
+ return new Promise((resolve, reject) => {
73
+ this.#schema = args;
74
+ const dbConnection = indexedDB.open(this.databaseName, this.dbVersion);
75
+ if (dbConnection != undefined) {
76
+ this.createCollections(args, this.dbVersion);
77
+ dbConnection.onsuccess = (event) => {
78
+ const db = event.target.result;
79
+ this.idbDatabase = db;
80
+ };
81
+ dbConnection.onupgradeneeded = async (event) => {
82
+ const db = event.target.result;
83
+ if (db) {
84
+ const m = new Schema();
85
+ m.createSchema(db, args);
86
+ }
87
+ else {
88
+ reject(true);
89
+ }
90
+ };
91
+ resolve(true);
92
+ }
93
+ else {
94
+ reject(true);
95
+ }
96
+ });
97
+ }
98
+ }
99
+ async transaction(storeNames, mode, callback) {
100
+ if (!this.idbDatabase) {
101
+ throw new Error("Database not initialized");
102
+ }
103
+ return new Promise((resolve, reject) => {
104
+ const tx = this.idbDatabase.transaction(storeNames, mode);
105
+ tx.onerror = () => reject(tx.error);
106
+ tx.oncomplete = () => resolve(result);
107
+ let result;
108
+ Promise.resolve(callback(tx)).then((value) => {
109
+ result = value;
110
+ }, (error) => {
111
+ tx.abort();
112
+ reject(error);
113
+ });
114
+ });
115
+ }
116
+ // @ts-ignore
117
+ createCollections(args, version) {
118
+ Object.keys(this.#schema).map(async (storeName) => {
119
+ Object.defineProperty(this, storeName, {
120
+ // @ts-ignore
121
+ value: new Collection(storeName, this.#schema[storeName], {
122
+ dbName: this.databaseName,
123
+ version, // @ts-ignore
124
+ }),
125
+ writable: true,
126
+ enumerable: true,
127
+ configurable: true,
128
+ });
129
+ });
130
+ }
131
+ }
132
+ /**
133
+ * Creates an IndexedDB database store schema for the given model and version.
134
+ *
135
+ * @param model - The IdbqModel representing the structure of the store.
136
+ * @param version - The version number of the store.
137
+ * @returns A function that creates a readonly collection for the given name.
138
+ * The returned function returns an instance of ReadonlyCollections<T> & typeof idb_.
139
+ *
140
+ * @typeparam T - The type of data stored in the collections.
141
+ */
142
+ export const createIdbqDb = (model, version) => {
143
+ return {
144
+ create: (name) => {
145
+ const idb_ = new IdbqlIndexedCore(name, model, version);
146
+ return {
147
+ idbDatabase: idb_,
148
+ idbql: idb_,
149
+ idbqlState: createIdbqlState(idb_).state,
150
+ idbqModel: model,
151
+ };
152
+ },
153
+ };
154
+ };
155
+ // main export is here ?
156
+ export const idbqBase = createIdbqDb;
@@ -0,0 +1,41 @@
1
+ /**
2
+ src\lib\scripts\idbqlCore\idbqlSchema.ts
3
+ * Represents a schema for IndexedDB.
4
+ */
5
+ /**
6
+ * Represents a schema for IndexedDB.
7
+ */
8
+ export declare class Schema {
9
+ /**
10
+ * Creates an object store in the database.
11
+ * @param db - The IDBDatabase instance.
12
+ * @param storeName - The name of the object store.
13
+ * @param keyPath - The key path for the object store.
14
+ * @param autoIncrement - Indicates whether the object store should have auto-incrementing keys. Default is false.
15
+ * @returns The created object store or null if creation fails.
16
+ */
17
+ createStore(db: IDBDatabase, storeName: string, keyPath: string, autoIncrement?: boolean): IDBObjectStore | null;
18
+ /**
19
+ * Creates the schema in the database.
20
+ * @param db - The IDBDatabase instance.
21
+ * @param storeListFields - An object containing the store names and their field configurations.
22
+ * @returns A promise that resolves to an array of store names and their key paths.
23
+ */
24
+ createSchema(db: IDBDatabase, storeListFields: Record<string, string>): Promise<Array<{
25
+ storeName: string;
26
+ keyPath: string;
27
+ }>>;
28
+ /**
29
+ * Cleans the index name by removing special characters.
30
+ * @param indexName - The index name to clean.
31
+ * @returns The cleaned index name.
32
+ */
33
+ private cleanIndexName;
34
+ /**
35
+ * Creates an index in the object store.
36
+ * @param store - The IDBObjectStore instance.
37
+ * @param indexName - The name of the index.
38
+ * @param keyPath - The key path for the index.
39
+ */
40
+ private createIndexes;
41
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ src\lib\scripts\idbqlCore\idbqlSchema.ts
3
+ * Represents a schema for IndexedDB.
4
+ */
5
+ /**
6
+ * Represents a schema for IndexedDB.
7
+ */
8
+ export class Schema {
9
+ /**
10
+ * Creates an object store in the database.
11
+ * @param db - The IDBDatabase instance.
12
+ * @param storeName - The name of the object store.
13
+ * @param keyPath - The key path for the object store.
14
+ * @param autoIncrement - Indicates whether the object store should have auto-incrementing keys. Default is false.
15
+ * @returns The created object store or null if creation fails.
16
+ */
17
+ createStore(db, storeName, keyPath, autoIncrement = false) {
18
+ try {
19
+ return db.createObjectStore(storeName, { keyPath, autoIncrement });
20
+ }
21
+ catch (error) {
22
+ console.error(`Failed to create store ${storeName}:`, error);
23
+ return null;
24
+ }
25
+ }
26
+ /**
27
+ * Creates the schema in the database.
28
+ * @param db - The IDBDatabase instance.
29
+ * @param storeListFields - An object containing the store names and their field configurations.
30
+ * @returns A promise that resolves to an array of store names and their key paths.
31
+ */
32
+ async createSchema(db, storeListFields) {
33
+ const results = [];
34
+ for (const [storeName, storeConfig] of Object.entries(storeListFields)) {
35
+ const fields = storeConfig.split(",").map((field) => field.trim());
36
+ const incrementField = fields
37
+ .find((field) => field.startsWith("++"))
38
+ ?.replace("++", "");
39
+ const declaredIndex = fields
40
+ .find((field) => field.startsWith("&"))
41
+ ?.replace("&", "");
42
+ const keyPath = incrementField || declaredIndex || fields[0];
43
+ const increment = Boolean(incrementField);
44
+ const store = this.createStore(db, storeName, keyPath, increment);
45
+ if (store) {
46
+ for (const field of fields) {
47
+ await this.createIndexes(store, field, field);
48
+ }
49
+ results.push({ storeName, keyPath });
50
+ }
51
+ }
52
+ return results;
53
+ }
54
+ /**
55
+ * Cleans the index name by removing special characters.
56
+ * @param indexName - The index name to clean.
57
+ * @returns The cleaned index name.
58
+ */
59
+ cleanIndexName(indexName) {
60
+ return indexName.replace(/[&+]/g, "").trim();
61
+ }
62
+ /**
63
+ * Creates an index in the object store.
64
+ * @param store - The IDBObjectStore instance.
65
+ * @param indexName - The name of the index.
66
+ * @param keyPath - The key path for the index.
67
+ */
68
+ async createIndexes(store, indexName, keyPath) {
69
+ try {
70
+ const cleanedIndexName = this.cleanIndexName(indexName);
71
+ const cleanedKeyPath = this.cleanIndexName(keyPath);
72
+ store.createIndex(cleanedIndexName, cleanedKeyPath);
73
+ }
74
+ catch (error) {
75
+ console.error(`Failed to create index ${indexName} for store ${store.name}:`, error);
76
+ }
77
+ }
78
+ }
@@ -0,0 +1,7 @@
1
+ export * from './collection/collection.js';
2
+ export * from './idbqlCore/idbqlCore.js';
3
+ export * from './idbqlCore/idbqlSchema.js';
4
+ export * from './path/pathResolver.js';
5
+ export * from './scripts/types.js';
6
+ export * from './state/idbqlEvent.svelte.js';
7
+ export * from './state/idbstate.svelte.js';
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ // Reexport of entry components
2
+ export * from './collection/collection.js';
3
+ export * from './idbqlCore/idbqlCore.js';
4
+ export * from './idbqlCore/idbqlSchema.js';
5
+ export * from './path/pathResolver.js';
6
+ export * from './scripts/types.js';
7
+ export * from './state/idbqlEvent.svelte.js';
8
+ export * from './state/idbstate.svelte.js';
@@ -0,0 +1,14 @@
1
+ export type DotPath<T> = T extends object ? {
2
+ [K in keyof T]: T[K] extends null | undefined ? K & string : `${K & string}${"" extends DotPath<T[K]> ? "" : "."}${DotPath<T[K]>}`;
3
+ }[keyof T] : "";
4
+ /**
5
+ * Resolves a dot path on an object and returns the value at that path.
6
+ * If the path does not exist, it returns the defaultValue.
7
+ *
8
+ * @template T - The type of the value to be returned.
9
+ * @param object - The object to resolve the path on.
10
+ * @param path - The dot path to resolve.
11
+ * @param defaultValue - The default value to return if the path does not exist. (optional)
12
+ * @returns The value at the specified path, or the defaultValue if the path does not exist.
13
+ */
14
+ export declare function dotPath<T = unknown>(object: Record<string, any>, path: string, defaultValue?: any): T;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Resolves a dot path on an object and returns the value at that path.
3
+ * If the path does not exist, it returns the defaultValue.
4
+ *
5
+ * @template T - The type of the value to be returned.
6
+ * @param object - The object to resolve the path on.
7
+ * @param path - The dot path to resolve.
8
+ * @param defaultValue - The default value to return if the path does not exist. (optional)
9
+ * @returns The value at the specified path, or the defaultValue if the path does not exist.
10
+ */
11
+ export function dotPath(object, path, defaultValue) {
12
+ return (path.split(".").reduce((r, s) => (r ? r[s] : defaultValue), object) ??
13
+ defaultValue);
14
+ }
@@ -0,0 +1,22 @@
1
+ export type Operator = keyof OperatorType;
2
+ export type OperatorType<T = any> = {
3
+ eq?: T;
4
+ gt?: T extends number | Date ? T : never;
5
+ gte?: T extends number | Date ? T : never;
6
+ lt?: T extends number | Date ? T : never;
7
+ lte?: T extends number | Date ? T : never;
8
+ ne?: T;
9
+ in?: T[];
10
+ nin?: T[];
11
+ contains?: T extends string ? string : never;
12
+ startsWith?: T extends string ? string : never;
13
+ endsWith?: T extends string ? string : never;
14
+ btw?: T extends number ? [T, T] : never;
15
+ };
16
+ type WhereCondition<T> = {
17
+ [K in keyof T]?: T[K] | Partial<OperatorType<T[K]>>;
18
+ };
19
+ export type Where<T = Record<string, any>> = WhereCondition<T> | {
20
+ [K in keyof OperatorType]?: Partial<T>;
21
+ };
22
+ export {};
@@ -0,0 +1,2 @@
1
+ /* path: D:\boulot\app-node\idbql\src\lib\scripts\types.ts */
2
+ export {};
@@ -0,0 +1,13 @@
1
+ type EventType = "add" | "put" | "update" | "updateWhere" | "delete" | "deleteWhere" | "set";
2
+ interface EventData<T = any> {
3
+ collection: string;
4
+ data: T;
5
+ keyPath: string;
6
+ }
7
+ declare class IdbqlStateEvent {
8
+ #private;
9
+ get dataState(): Record<string, any[]>;
10
+ registerEvent(event: EventType, eventData: EventData): void;
11
+ }
12
+ export declare const idbqlEvent: IdbqlStateEvent;
13
+ export {};
@@ -0,0 +1,63 @@
1
+ class IdbqlStateEvent {
2
+ #dataState = $state({});
3
+ get dataState() {
4
+ return this.#dataState;
5
+ }
6
+ registerEvent(event, eventData) {
7
+ const { collection, data, keyPath } = eventData;
8
+ if (!collection) {
9
+ console.error(`Collection is mandatory`);
10
+ return;
11
+ }
12
+ if (!this.#dataState[collection]) {
13
+ this.#dataState[collection] = [];
14
+ }
15
+ switch (event) {
16
+ case "set":
17
+ if (data) {
18
+ this.#dataState[collection] = Array.isArray(data) ? data : [data];
19
+ }
20
+ break;
21
+ case "add":
22
+ if (data) {
23
+ this.#dataState[collection].push(data);
24
+ }
25
+ break;
26
+ case "put":
27
+ case "update":
28
+ if (data && keyPath) {
29
+ const index = this.#dataState[collection].findIndex((item) => item[keyPath] === data[keyPath]);
30
+ if (index !== -1) {
31
+ this.#dataState[collection][index] = {
32
+ ...this.#dataState[collection][index],
33
+ ...data,
34
+ };
35
+ }
36
+ else {
37
+ this.#dataState[collection].push(data);
38
+ }
39
+ }
40
+ break;
41
+ case "updateWhere":
42
+ if (data && typeof data === "object") {
43
+ this.#dataState[collection] = this.#dataState[collection].map((item) => Object.entries(data).every(([key, value]) => item[key] === value)
44
+ ? { ...item, ...data }
45
+ : item);
46
+ }
47
+ break;
48
+ case "delete":
49
+ if (data && keyPath) {
50
+ this.#dataState[collection] = this.#dataState[collection].filter((item) => item[keyPath] !== data[keyPath]);
51
+ }
52
+ break;
53
+ case "deleteWhere":
54
+ if (data && typeof data === "object") {
55
+ this.#dataState[collection] = this.#dataState[collection].filter((item) => !Object.entries(data).every(([key, value]) => item[key] === value));
56
+ }
57
+ break;
58
+ default:
59
+ console.error(`Unhandled event type: ${event}`);
60
+ }
61
+ }
62
+ }
63
+ export const idbqlEvent = new IdbqlStateEvent();
@@ -0,0 +1,41 @@
1
+ import type { IdbqlIndexedCore } from "../idbqlCore/idbqlCore.js";
2
+ import { type Where, type ResultSet, type ResultsetOptions } from "@medyll/idae-query";
3
+ /**
4
+ * Main entry point.
5
+ * Creates a state object with indexedDB synchronization.
6
+ * @param {IdbqlIndexedCore} [idbBase] - The IdbqlCore instance.
7
+ * @returns {object} - The state object.
8
+ */
9
+ export declare const createIdbqlState: (idbBase: IdbqlIndexedCore) => {
10
+ readonly state: Record<string, any>;
11
+ onCollection: <T>(collectionName: string) => StateCollectionDyn<T>;
12
+ addCollection: <T>(collectionName: string) => StateCollectionDyn<T>;
13
+ };
14
+ /**
15
+ * Adds a collection to the svelte 5 state and synchronize with indexedDB if it exists.
16
+ * @template T - The type of data in the collection.
17
+ * @param {string} collectionName - The name of the collection.
18
+ * @param {string} [keyPath="id"] - The key path for the collection.
19
+ * @returns {Proxy} - A proxy object with methods to interact with the collection.
20
+ */
21
+ export declare class StateCollectionDyn<T> {
22
+ private collectionName;
23
+ private state;
24
+ private idbBase;
25
+ constructor(collectionName: string, idbBase: IdbqlIndexedCore);
26
+ get collectionState(): any;
27
+ private testIdbql;
28
+ private feed;
29
+ where(qy: Where<T>, options?: ResultsetOptions): ResultSet<T>;
30
+ update(keyPathValue: string | number, data: Partial<T>): void;
31
+ get(value: any, pathKey?: string): T[];
32
+ getOne(value: any, pathKey?: string): T;
33
+ getAll(): ResultSet<T>;
34
+ updateWhere(where: Where<T>, data: Partial<T>): void;
35
+ put(value: Partial<T>): void;
36
+ add(data: T): void;
37
+ delete(keyPathValue: string | number): boolean | undefined;
38
+ /** @deprecated */
39
+ del(keyPathValue: string | number): boolean | undefined;
40
+ deleteWhere(where: Where<T>): boolean | undefined;
41
+ }
@@ -0,0 +1,124 @@
1
+ import { idbqlEvent } from "./idbqlEvent.svelte.js";
2
+ //
3
+ import { Operators, getResultset, } from "@medyll/idae-query";
4
+ //
5
+ /**
6
+ * Main entry point.
7
+ * Creates a state object with indexedDB synchronization.
8
+ * @param {IdbqlIndexedCore} [idbBase] - The IdbqlCore instance.
9
+ * @returns {object} - The state object.
10
+ */
11
+ export const createIdbqlState = (idbBase) => {
12
+ let collections = {};
13
+ if (idbBase.schema) {
14
+ addCollections(idbBase.schema);
15
+ }
16
+ /**
17
+ * Adds a collection to the svelte 5 state and synchronize with indexedDB if it exists.
18
+ * @template T - The type of data in the collection.
19
+ * @param {string} collectionName - The name of the collection.
20
+ * @param {string} [keyPath="id"] - The key path for the collection.
21
+ * @returns {Proxy} - A proxy object with methods to interact with the collection.
22
+ */
23
+ function addCollection(collectionName) {
24
+ return new StateCollectionDyn(collectionName, idbBase);
25
+ }
26
+ function addCollections(args) {
27
+ Object.keys(args).map((collection) => {
28
+ const t = args[collection];
29
+ collections[collection] = addCollection(collection);
30
+ });
31
+ }
32
+ return {
33
+ get state() {
34
+ return collections;
35
+ },
36
+ onCollection: addCollection,
37
+ addCollection: addCollection,
38
+ };
39
+ };
40
+ /**
41
+ * Adds a collection to the svelte 5 state and synchronize with indexedDB if it exists.
42
+ * @template T - The type of data in the collection.
43
+ * @param {string} collectionName - The name of the collection.
44
+ * @param {string} [keyPath="id"] - The key path for the collection.
45
+ * @returns {Proxy} - A proxy object with methods to interact with the collection.
46
+ */
47
+ export class StateCollectionDyn {
48
+ collectionName;
49
+ state = idbqlEvent.dataState; // svelte state ;)
50
+ idbBase;
51
+ constructor(collectionName, idbBase) {
52
+ if (!this.state?.[collectionName])
53
+ this.state[collectionName] = [];
54
+ this.collectionName = collectionName;
55
+ this.idbBase = idbBase;
56
+ this.feed();
57
+ return this;
58
+ }
59
+ get collectionState() {
60
+ return this.state[this.collectionName];
61
+ }
62
+ testIdbql(collection) {
63
+ return this.idbBase && Boolean(this.idbBase?.[collection]);
64
+ }
65
+ feed() {
66
+ if (this.idbBase && this.testIdbql(this.collectionName)) {
67
+ this.idbBase[this.collectionName].getAll().then((data) => {
68
+ this.state[this.collectionName] = data;
69
+ });
70
+ }
71
+ }
72
+ where(qy, options) {
73
+ let c = Operators.parse(this.collectionState ?? [], qy);
74
+ const r = getResultset(c);
75
+ if (options)
76
+ r.setOptions(options);
77
+ return r;
78
+ }
79
+ update(keyPathValue, data) {
80
+ if (this.idbBase && this.testIdbql(this.collectionName)) {
81
+ this.idbBase[this.collectionName].update(keyPathValue, data);
82
+ }
83
+ }
84
+ get(value, pathKey = "id") {
85
+ return this.collectionState.filter((d) => d[pathKey] === value);
86
+ }
87
+ getOne(value, pathKey = "id") {
88
+ return this.collectionState.filter((d) => d[pathKey] === value)?.[0];
89
+ }
90
+ getAll() {
91
+ return getResultset(this.collectionState);
92
+ }
93
+ updateWhere(where, data) {
94
+ if (this.idbBase && this.testIdbql(this.collectionName)) {
95
+ this.idbBase[this.collectionName].updateWhere(where, data);
96
+ }
97
+ }
98
+ put(value) {
99
+ if (this.idbBase && this.testIdbql(this.collectionName)) {
100
+ this.idbBase[this.collectionName].put(value);
101
+ }
102
+ }
103
+ add(data) {
104
+ if (this.idbBase && this.testIdbql(this.collectionName)) {
105
+ this.idbBase[this.collectionName].add(data);
106
+ }
107
+ }
108
+ delete(keyPathValue) {
109
+ if (this.idbBase && this.testIdbql(this.collectionName)) {
110
+ return this.idbBase[this.collectionName].delete(keyPathValue);
111
+ }
112
+ }
113
+ /** @deprecated */
114
+ del(keyPathValue) {
115
+ if (this.idbBase && this.testIdbql(this.collectionName)) {
116
+ return this.idbBase[this.collectionName].delete(keyPathValue);
117
+ }
118
+ }
119
+ deleteWhere(where) {
120
+ if (this.idbBase && this.testIdbql(this.collectionName)) {
121
+ return this.idbBase[this.collectionName].deleteWhere(where);
122
+ }
123
+ }
124
+ }
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@medyll/idae-idbql",
3
+ "scope": "@medyll",
4
+ "version": "0.0.1",
5
+ "scripts": {
6
+ "dev": "vite dev",
7
+ "build": "vite build && npm run package",
8
+ "preview": "vite preview",
9
+ "package": "svelte-kit sync && svelte-package && publint",
10
+ "prepublishOnly": "npm run package",
11
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
12
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
13
+ },
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "svelte": "./dist/index.js"
18
+ }
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "!dist/**/*.test.*",
23
+ "!dist/**/*.spec.*"
24
+ ],
25
+ "peerDependencies": {
26
+ "svelte": "^5.0.0-next.0"
27
+ },
28
+ "devDependencies": {
29
+ "@sveltejs/adapter-auto": "^3.0.0",
30
+ "@sveltejs/kit": "^2.0.0",
31
+ "@sveltejs/package": "^2.0.0",
32
+ "@sveltejs/vite-plugin-svelte": "^3.0.0",
33
+ "publint": "^0.1.9",
34
+ "svelte": "^5.0.0-next.181",
35
+ "svelte-check": "^3.6.0",
36
+ "tslib": "^2.4.1",
37
+ "typescript": "^5.0.0",
38
+ "vite": "^5.0.11"
39
+ },
40
+ "svelte": "./dist/index.js",
41
+ "types": "./dist/index.d.ts",
42
+ "type": "module",
43
+ "dependencies": {
44
+ "@medyll/idae-query": "^0.0.1"
45
+ }
46
+ }