@medyll/idae-query 0.0.1 → 0.1.0
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 +156 -30
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/path/pathResolver.d.ts +2 -2
- package/dist/path/pathResolver.js +1 -2
- package/dist/query/query.d.ts +1 -1
- package/dist/{resultset → resultSet}/Readme.md +39 -39
- package/dist/{resultset → resultSet}/resultset.d.ts +3 -3
- package/dist/{resultset → resultSet}/resultset.js +9 -9
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -1,58 +1,184 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @medyll/idae-idbql
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A powerful and flexible IndexedDB query library for TypeScript and JavaScript applications.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Features
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- MongoDB-like query interface for IndexedDB
|
|
8
|
+
- Strong TypeScript support with full type inference
|
|
9
|
+
- Reactive state management for real-time UI updates
|
|
10
|
+
- Support for complex CRUD operations and advanced querying
|
|
11
|
+
- Flexible data modeling with automatic schema creation
|
|
12
|
+
- Built-in indexing and optimization features
|
|
13
|
+
- Easy integration with front-end frameworks, especially Svelte
|
|
14
|
+
- Robust error handling and logging
|
|
15
|
+
- Versioning and database migration support
|
|
16
|
+
- Support for svelte 5 state
|
|
8
17
|
|
|
9
|
-
|
|
18
|
+
## Installation
|
|
10
19
|
|
|
11
20
|
```bash
|
|
12
|
-
|
|
13
|
-
|
|
21
|
+
npm install @medyll/idae-idbql
|
|
22
|
+
```
|
|
14
23
|
|
|
15
|
-
|
|
16
|
-
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { createIdbqDb } from '@medyll/idae-idbql';
|
|
28
|
+
|
|
29
|
+
// Define your data model
|
|
30
|
+
const exampleModel = {
|
|
31
|
+
messages: {
|
|
32
|
+
keyPath: "++id, chatId, created_at",
|
|
33
|
+
ts: {} as ChatMessage,
|
|
34
|
+
},
|
|
35
|
+
chat: {
|
|
36
|
+
keyPath: "&chatId, created_at, dateLastMessage",
|
|
37
|
+
ts: {} as Chat,
|
|
38
|
+
template: {},
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Create a database instance
|
|
43
|
+
const idbqStore = createIdbqDb(exampleModel, 1);
|
|
44
|
+
const { idbql, idbqlState, idbDatabase, idbqModel } = idbqStore.create("myDatabase");
|
|
45
|
+
|
|
46
|
+
// Perform database operations
|
|
47
|
+
async function fetchMessages() {
|
|
48
|
+
const messages = await idbql.messages.where({ chatId: "123" }).toArray();
|
|
49
|
+
console.log(messages);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
fetchMessages();
|
|
17
53
|
```
|
|
18
54
|
|
|
19
|
-
##
|
|
55
|
+
## API Reference
|
|
20
56
|
|
|
21
|
-
|
|
57
|
+
### createIdbqDb(model, version)
|
|
22
58
|
|
|
23
|
-
|
|
24
|
-
|
|
59
|
+
Creates an IndexedDB database instance with the specified model and version.
|
|
60
|
+
|
|
61
|
+
### idbql
|
|
62
|
+
|
|
63
|
+
The main interface for database operations. Provides methods for each collection defined in your model.
|
|
64
|
+
|
|
65
|
+
### idbqlState
|
|
66
|
+
|
|
67
|
+
A reactive state object that reflects the current state of your database.
|
|
68
|
+
|
|
69
|
+
### idbDatabase
|
|
70
|
+
|
|
71
|
+
Provides low-level access to the IndexedDB instance.
|
|
25
72
|
|
|
26
|
-
|
|
27
|
-
|
|
73
|
+
### idbqModel
|
|
74
|
+
|
|
75
|
+
Contains the database model definition.
|
|
76
|
+
|
|
77
|
+
## Query Operations
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
// Add a new item
|
|
81
|
+
await idbql.messages.add({ chatId: "123", content: "Hello" });
|
|
82
|
+
|
|
83
|
+
// Update an item
|
|
84
|
+
await idbql.messages.put({ id: 1, content: "Updated message" });
|
|
85
|
+
|
|
86
|
+
// Delete an item
|
|
87
|
+
await idbql.messages.delete(1);
|
|
88
|
+
|
|
89
|
+
// Query items
|
|
90
|
+
const recentMessages = await idbql.messages
|
|
91
|
+
.where({ created_at: { gt: new Date(Date.now() - 86400000) } })
|
|
92
|
+
.toArray();
|
|
28
93
|
```
|
|
29
94
|
|
|
30
|
-
|
|
95
|
+
## Transactions
|
|
31
96
|
|
|
32
|
-
|
|
97
|
+
idbql supports complex transactions across multiple object stores:
|
|
33
98
|
|
|
34
|
-
|
|
99
|
+
```typescript
|
|
100
|
+
const result = await idbql.transaction(
|
|
101
|
+
["users", "posts"],
|
|
102
|
+
"readwrite",
|
|
103
|
+
async (tx) => {
|
|
104
|
+
const userStore = tx.objectStore("users");
|
|
105
|
+
const postStore = tx.objectStore("posts");
|
|
35
106
|
|
|
36
|
-
|
|
37
|
-
|
|
107
|
+
const userId = await userStore.add({ name: "Alice", email: "alice@example.com" });
|
|
108
|
+
const postId = await postStore.add({ userId, title: "Alice's First Post", content: "Hello, World!" });
|
|
109
|
+
|
|
110
|
+
return { userId, postId };
|
|
111
|
+
}
|
|
112
|
+
);
|
|
38
113
|
```
|
|
39
114
|
|
|
40
|
-
|
|
115
|
+
## Reactive State Management
|
|
41
116
|
|
|
42
|
-
```
|
|
43
|
-
|
|
117
|
+
```typescript
|
|
118
|
+
import { derived } from 'svelte/store';
|
|
119
|
+
|
|
120
|
+
const activeUsers = $derived(idbqlState.users.where({ isActive: true }));
|
|
44
121
|
```
|
|
45
122
|
|
|
46
|
-
|
|
123
|
+
## Integration with Svelte
|
|
47
124
|
|
|
48
|
-
|
|
125
|
+
```svelte
|
|
126
|
+
<script>
|
|
127
|
+
import { derived } from 'svelte/store';
|
|
128
|
+
import { idbqlState } from './store';
|
|
49
129
|
|
|
50
|
-
|
|
130
|
+
const messages = $derived(idbqlState.messages.where({ chatId: "123" }));
|
|
131
|
+
</script>
|
|
51
132
|
|
|
52
|
-
|
|
133
|
+
{#each $messages as message}
|
|
134
|
+
<p>{message.content}</p>
|
|
135
|
+
{/each}
|
|
136
|
+
```
|
|
53
137
|
|
|
54
|
-
|
|
138
|
+
## Versioning and Migrations
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
const idbqStore = createIdbqDb(myModel, 2);
|
|
142
|
+
const { idbDatabase } = idbqStore.create("myDb", {
|
|
143
|
+
upgrade(oldVersion, newVersion, transaction) {
|
|
144
|
+
if (oldVersion < 2) {
|
|
145
|
+
const userStore = transaction.objectStore("users");
|
|
146
|
+
userStore.createIndex("emailIndex", "email", { unique: true });
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
```
|
|
55
151
|
|
|
56
|
-
|
|
57
|
-
|
|
152
|
+
## Error Handling
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
try {
|
|
156
|
+
await idbql.users.add({ username: "existing_user" });
|
|
157
|
+
} catch (error) {
|
|
158
|
+
if (error instanceof UniqueConstraintError) {
|
|
159
|
+
console.error("Username already exists");
|
|
160
|
+
} else {
|
|
161
|
+
console.error("An unexpected error occurred", error);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
58
164
|
```
|
|
165
|
+
|
|
166
|
+
## Performance Tips
|
|
167
|
+
|
|
168
|
+
- Use appropriate indexes
|
|
169
|
+
- Limit result sets with `.limit(n)`
|
|
170
|
+
- Use `.count()` instead of `.toArray().length`
|
|
171
|
+
- Optimize queries to use indexes effectively
|
|
172
|
+
|
|
173
|
+
## Contributing
|
|
174
|
+
|
|
175
|
+
Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
|
|
176
|
+
|
|
177
|
+
## License
|
|
178
|
+
|
|
179
|
+
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
|
|
180
|
+
|
|
181
|
+
## Support
|
|
182
|
+
|
|
183
|
+
If you encounter any issues or have questions, please file an issue on the GitHub repository.
|
|
184
|
+
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type DotPath<T> = T extends object ? {
|
|
2
|
-
[K in keyof T]: T[K] extends null | undefined ? K & string : `${K & string}${
|
|
3
|
-
}[keyof T] :
|
|
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
4
|
/**
|
|
5
5
|
* Resolves a dot path on an object and returns the value at that path.
|
|
6
6
|
* If the path does not exist, it returns the defaultValue.
|
|
@@ -9,6 +9,5 @@
|
|
|
9
9
|
* @returns The value at the specified path, or the defaultValue if the path does not exist.
|
|
10
10
|
*/
|
|
11
11
|
export function dotPath(object, path, defaultValue) {
|
|
12
|
-
return (path.split(
|
|
13
|
-
defaultValue);
|
|
12
|
+
return (path.split('.').reduce((r, s) => (r ? r[s] : defaultValue), object) ?? defaultValue);
|
|
14
13
|
}
|
package/dist/query/query.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Where } from '../types.js';
|
|
|
2
2
|
export declare class Query<T extends object> {
|
|
3
3
|
data: T[];
|
|
4
4
|
constructor(data: T[]);
|
|
5
|
-
where(qy: Where<T>):
|
|
5
|
+
where(qy: Where<T>): any;
|
|
6
6
|
private matchesQuery;
|
|
7
7
|
private matchesField;
|
|
8
8
|
}
|
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
## ResultSet.ts
|
|
2
|
-
|
|
3
|
-
This class `ResultSet` is used to manipulate and iterate over a set of data. The class is iterable, meaning it can be spread into an array or iterated over with a `for...of` loop.
|
|
4
|
-
|
|
5
|
-
### Methods
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
#### setOptions<T>(options: OptionsType)
|
|
10
|
-
|
|
11
|
-
- Action: Sets the options for the result set in one pass.
|
|
12
|
-
- Arguments: An `OptionsType` object which can contain `sort`, `page`, and `groupBy` properties.
|
|
13
|
-
- `sort`: An object where the keys represent the properties to sort by, and the values represent the sort order ("asc" for ascending, "desc" for descending).
|
|
14
|
-
- `page`: An object with `size` and `number` properties, representing the number of items per page and the page number to retrieve, respectively.
|
|
15
|
-
- `groupBy`: A string or an array of strings, representing the field names to group by.
|
|
16
|
-
- Return: The updated result set.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
#### sortBy(args: Record<string, "asc" | "desc">)
|
|
20
|
-
|
|
21
|
-
- Action: Sorts the data in the result set based on the provided sorting criteria.
|
|
22
|
-
- Arguments: An object where the keys represent the properties to sort by, and the values represent the sort order ("asc" for ascending, "desc" for descending).
|
|
23
|
-
- Return: The sorted result set.
|
|
24
|
-
|
|
25
|
-
#### getPage(size: number, page: number)
|
|
26
|
-
|
|
27
|
-
- Action: Retrieves a specific page of data from the result set.
|
|
28
|
-
- Arguments: The `size` parameter specifies the number of items per page, and the `page` parameter specifies the page number to retrieve.
|
|
29
|
-
- Return: A new result set containing the specified page of data.
|
|
30
|
-
|
|
31
|
-
#### groupBy(fieldName: string | string[], transform?: (value: any) => void)
|
|
32
|
-
|
|
33
|
-
- Action: Groups the result set by the specified field name(s).
|
|
34
|
-
- Arguments: The `fieldName` parameter can be a string or an array of strings, representing the field names to group by. The `transform` parameter is an optional transformation function to apply to each grouped value.
|
|
35
|
-
- Return: An object representing the grouped result set.
|
|
36
|
-
|
|
37
|
-
### Iterability
|
|
38
|
-
|
|
39
|
-
The `ResultSet` class is iterable, meaning it can be spread into an array or iterated over with a `for...of` loop. This is achieved by implementing the `[Symbol.iterator]` method.
|
|
1
|
+
## ResultSet.ts
|
|
2
|
+
|
|
3
|
+
This class `ResultSet` is used to manipulate and iterate over a set of data. The class is iterable, meaning it can be spread into an array or iterated over with a `for...of` loop.
|
|
4
|
+
|
|
5
|
+
### Methods
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
#### setOptions<T>(options: OptionsType)
|
|
10
|
+
|
|
11
|
+
- Action: Sets the options for the result set in one pass.
|
|
12
|
+
- Arguments: An `OptionsType` object which can contain `sort`, `page`, and `groupBy` properties.
|
|
13
|
+
- `sort`: An object where the keys represent the properties to sort by, and the values represent the sort order ("asc" for ascending, "desc" for descending).
|
|
14
|
+
- `page`: An object with `size` and `number` properties, representing the number of items per page and the page number to retrieve, respectively.
|
|
15
|
+
- `groupBy`: A string or an array of strings, representing the field names to group by.
|
|
16
|
+
- Return: The updated result set.
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
#### sortBy(args: Record<string, "asc" | "desc">)
|
|
20
|
+
|
|
21
|
+
- Action: Sorts the data in the result set based on the provided sorting criteria.
|
|
22
|
+
- Arguments: An object where the keys represent the properties to sort by, and the values represent the sort order ("asc" for ascending, "desc" for descending).
|
|
23
|
+
- Return: The sorted result set.
|
|
24
|
+
|
|
25
|
+
#### getPage(size: number, page: number)
|
|
26
|
+
|
|
27
|
+
- Action: Retrieves a specific page of data from the result set.
|
|
28
|
+
- Arguments: The `size` parameter specifies the number of items per page, and the `page` parameter specifies the page number to retrieve.
|
|
29
|
+
- Return: A new result set containing the specified page of data.
|
|
30
|
+
|
|
31
|
+
#### groupBy(fieldName: string | string[], transform?: (value: any) => void)
|
|
32
|
+
|
|
33
|
+
- Action: Groups the result set by the specified field name(s).
|
|
34
|
+
- Arguments: The `fieldName` parameter can be a string or an array of strings, representing the field names to group by. The `transform` parameter is an optional transformation function to apply to each grouped value.
|
|
35
|
+
- Return: An object representing the grouped result set.
|
|
36
|
+
|
|
37
|
+
### Iterability
|
|
38
|
+
|
|
39
|
+
The `ResultSet` class is iterable, meaning it can be spread into an array or iterated over with a `for...of` loop. This is achieved by implementing the `[Symbol.iterator]` method.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { type DotPath } from
|
|
1
|
+
import { type DotPath } from '../path/pathResolver.js';
|
|
2
2
|
/**
|
|
3
3
|
* Represents the options for a result set.
|
|
4
4
|
* @template T - The type of the result set.
|
|
5
5
|
*/
|
|
6
6
|
export type ResultsetOptions<T = any> = {
|
|
7
7
|
/** Can receive a dot path for sorting. */
|
|
8
|
-
sort?: Record<DotPath<T>,
|
|
8
|
+
sort?: Record<DotPath<T>, 'asc' | 'desc'>;
|
|
9
9
|
/** Specifies the property to group the result set by. */
|
|
10
10
|
groupBy?: DotPath<T>;
|
|
11
11
|
/** Specifies the page size of the result set. */
|
|
@@ -18,7 +18,7 @@ export type ResultsetOptions<T = any> = {
|
|
|
18
18
|
export type ResultSet<T> = T[] & {
|
|
19
19
|
setOptions: (options: ResultsetOptions) => ResultSet<T>;
|
|
20
20
|
/** Accepts a dot path */
|
|
21
|
-
sortBy: (args: Record<string,
|
|
21
|
+
sortBy: (args: Record<string, 'asc' | 'desc'>) => ResultSet<T>;
|
|
22
22
|
/** Accepts a dot path as fieldName */
|
|
23
23
|
groupBy: (fieldName: string | string[],
|
|
24
24
|
/** Transformer function to generate the grouped key */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { dotPath } from
|
|
1
|
+
import { dotPath } from '../path/pathResolver.js';
|
|
2
2
|
/**
|
|
3
3
|
* Generates a ResultSet based on the provided data array and defines additional properties like setOptions, sortBy, groupBy, and getPage for customization and manipulation.
|
|
4
4
|
*
|
|
@@ -19,7 +19,7 @@ export function getResultset(data) {
|
|
|
19
19
|
return this;
|
|
20
20
|
},
|
|
21
21
|
enumerable: false,
|
|
22
|
-
configurable: true
|
|
22
|
+
configurable: true
|
|
23
23
|
},
|
|
24
24
|
sortBy: {
|
|
25
25
|
value: function (args) {
|
|
@@ -31,7 +31,7 @@ export function getResultset(data) {
|
|
|
31
31
|
while (i < keys.length && result === 0) {
|
|
32
32
|
let value = keys[i];
|
|
33
33
|
result =
|
|
34
|
-
values[i] ===
|
|
34
|
+
values[i] === 'asc'
|
|
35
35
|
? Number(dotPath(a, value)) - Number(dotPath(b, value))
|
|
36
36
|
: Number(dotPath(b, value)) - Number(dotPath(a, value));
|
|
37
37
|
i++;
|
|
@@ -42,13 +42,13 @@ export function getResultset(data) {
|
|
|
42
42
|
return this;
|
|
43
43
|
},
|
|
44
44
|
enumerable: false,
|
|
45
|
-
configurable: true
|
|
45
|
+
configurable: true
|
|
46
46
|
},
|
|
47
47
|
groupBy: {
|
|
48
48
|
value: function (fieldName, transform) {
|
|
49
|
-
const finalFieldName = typeof fieldName ===
|
|
49
|
+
const finalFieldName = typeof fieldName === 'string' ? [fieldName] : fieldName;
|
|
50
50
|
return this.reduce((acc, curr) => {
|
|
51
|
-
let key =
|
|
51
|
+
let key = '';
|
|
52
52
|
for (let i = 0; i < finalFieldName.length; i++) {
|
|
53
53
|
key += dotPath(curr, finalFieldName[i]);
|
|
54
54
|
}
|
|
@@ -60,7 +60,7 @@ export function getResultset(data) {
|
|
|
60
60
|
}, {});
|
|
61
61
|
},
|
|
62
62
|
enumerable: false,
|
|
63
|
-
configurable: true
|
|
63
|
+
configurable: true
|
|
64
64
|
},
|
|
65
65
|
getPage: {
|
|
66
66
|
value: function (page, size) {
|
|
@@ -69,8 +69,8 @@ export function getResultset(data) {
|
|
|
69
69
|
return dta;
|
|
70
70
|
},
|
|
71
71
|
enumerable: false,
|
|
72
|
-
configurable: true
|
|
73
|
-
}
|
|
72
|
+
configurable: true
|
|
73
|
+
}
|
|
74
74
|
});
|
|
75
75
|
return data;
|
|
76
76
|
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@medyll/idae-query",
|
|
3
3
|
"scope": "@medyll",
|
|
4
|
-
"version": "0.0
|
|
4
|
+
"version": "0.1.0",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "vite dev",
|
|
7
7
|
"build": "vite build && npm run package",
|
|
8
8
|
"preview": "vite preview",
|
|
9
9
|
"package": "svelte-kit sync && svelte-package && publint",
|
|
10
|
+
"package:watch": "svelte-kit sync && svelte-package --watch",
|
|
10
11
|
"prepublishOnly": "npm run package",
|
|
11
12
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
12
13
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
@@ -26,9 +27,10 @@
|
|
|
26
27
|
"!dist/**/*.spec.*"
|
|
27
28
|
],
|
|
28
29
|
"peerDependencies": {
|
|
29
|
-
"svelte": "^
|
|
30
|
+
"svelte": "^5.0.0-next.1"
|
|
30
31
|
},
|
|
31
32
|
"devDependencies": {
|
|
33
|
+
"@medyll/idae-prettier-config": "^1.0.0",
|
|
32
34
|
"@sveltejs/adapter-auto": "^3.0.0",
|
|
33
35
|
"@sveltejs/kit": "^2.0.0",
|
|
34
36
|
"@sveltejs/package": "^2.0.0",
|
|
@@ -41,7 +43,7 @@
|
|
|
41
43
|
"prettier": "^3.1.1",
|
|
42
44
|
"prettier-plugin-svelte": "^3.1.2",
|
|
43
45
|
"publint": "^0.1.9",
|
|
44
|
-
"svelte": "^5.0.0-next.
|
|
46
|
+
"svelte": "^5.0.0-next.183",
|
|
45
47
|
"svelte-check": "^3.6.0",
|
|
46
48
|
"tslib": "^2.4.1",
|
|
47
49
|
"typescript": "^5.0.0",
|
|
@@ -52,4 +54,4 @@
|
|
|
52
54
|
"svelte": "./dist/index.js",
|
|
53
55
|
"types": "./dist/index.d.ts",
|
|
54
56
|
"type": "module"
|
|
55
|
-
}
|
|
57
|
+
}
|