@decaf-ts/for-couchdb 0.3.0 → 0.3.2

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.
Files changed (93) hide show
  1. package/LICENSE.md +646 -144
  2. package/README.md +371 -1
  3. package/dist/for-couchdb.cjs +907 -326
  4. package/dist/for-couchdb.esm.cjs +910 -329
  5. package/lib/adapter.cjs +249 -41
  6. package/lib/adapter.d.ts +243 -17
  7. package/lib/constants.cjs +30 -2
  8. package/lib/constants.d.ts +28 -0
  9. package/lib/errors.cjs +19 -2
  10. package/lib/errors.d.ts +17 -0
  11. package/lib/esm/adapter.d.ts +243 -17
  12. package/lib/esm/adapter.js +249 -41
  13. package/lib/esm/constants.d.ts +28 -0
  14. package/lib/esm/constants.js +30 -2
  15. package/lib/esm/errors.d.ts +17 -0
  16. package/lib/esm/errors.js +19 -2
  17. package/lib/esm/index.d.ts +6 -13
  18. package/lib/esm/index.js +7 -14
  19. package/lib/esm/indexes/generator.d.ts +47 -0
  20. package/lib/esm/indexes/generator.js +58 -1
  21. package/lib/esm/interfaces/CouchDBRepository.d.ts +10 -0
  22. package/lib/esm/interfaces/CouchDBRepository.js +1 -1
  23. package/lib/esm/model/CouchDBSequence.d.ts +15 -9
  24. package/lib/esm/model/CouchDBSequence.js +12 -1
  25. package/lib/esm/query/Paginator.d.ts +117 -4
  26. package/lib/esm/query/Paginator.js +128 -23
  27. package/lib/esm/query/Statement.d.ts +141 -8
  28. package/lib/esm/query/Statement.js +256 -28
  29. package/lib/esm/query/constants.d.ts +43 -0
  30. package/lib/esm/query/constants.js +44 -1
  31. package/lib/esm/query/index.d.ts +4 -1
  32. package/lib/esm/query/index.js +5 -2
  33. package/lib/esm/query/translate.d.ts +31 -0
  34. package/lib/esm/query/translate.js +32 -1
  35. package/lib/esm/sequences/Sequence.d.ts +0 -2
  36. package/lib/esm/sequences/Sequence.js +5 -9
  37. package/lib/esm/types.d.ts +55 -12
  38. package/lib/esm/types.js +1 -1
  39. package/lib/esm/utils.d.ts +105 -0
  40. package/lib/esm/utils.js +106 -1
  41. package/lib/index.cjs +7 -14
  42. package/lib/index.d.ts +6 -13
  43. package/lib/indexes/generator.cjs +58 -1
  44. package/lib/indexes/generator.d.ts +47 -0
  45. package/lib/interfaces/CouchDBRepository.cjs +1 -1
  46. package/lib/interfaces/CouchDBRepository.d.ts +10 -0
  47. package/lib/model/CouchDBSequence.cjs +12 -1
  48. package/lib/model/CouchDBSequence.d.ts +15 -9
  49. package/lib/query/Paginator.cjs +126 -21
  50. package/lib/query/Paginator.d.ts +117 -4
  51. package/lib/query/Statement.cjs +255 -27
  52. package/lib/query/Statement.d.ts +141 -8
  53. package/lib/query/constants.cjs +45 -2
  54. package/lib/query/constants.d.ts +43 -0
  55. package/lib/query/index.cjs +5 -2
  56. package/lib/query/index.d.ts +4 -1
  57. package/lib/query/translate.cjs +32 -1
  58. package/lib/query/translate.d.ts +31 -0
  59. package/lib/sequences/Sequence.cjs +5 -9
  60. package/lib/sequences/Sequence.d.ts +0 -2
  61. package/lib/types.cjs +1 -1
  62. package/lib/types.d.ts +55 -12
  63. package/lib/utils.cjs +106 -1
  64. package/lib/utils.d.ts +105 -0
  65. package/package.json +2 -2
  66. package/lib/esm/query/FromClause.d.ts +0 -7
  67. package/lib/esm/query/FromClause.js +0 -20
  68. package/lib/esm/query/InsertClause.d.ts +0 -7
  69. package/lib/esm/query/InsertClause.js +0 -13
  70. package/lib/esm/query/SelectClause.d.ts +0 -7
  71. package/lib/esm/query/SelectClause.js +0 -16
  72. package/lib/esm/query/ValuesClause.d.ts +0 -7
  73. package/lib/esm/query/ValuesClause.js +0 -12
  74. package/lib/esm/query/WhereClause.d.ts +0 -7
  75. package/lib/esm/query/WhereClause.js +0 -57
  76. package/lib/esm/query/factory.d.ts +0 -25
  77. package/lib/esm/query/factory.js +0 -117
  78. package/lib/esm/sequences/utils.d.ts +0 -1
  79. package/lib/esm/sequences/utils.js +0 -16
  80. package/lib/query/FromClause.cjs +0 -24
  81. package/lib/query/FromClause.d.ts +0 -7
  82. package/lib/query/InsertClause.cjs +0 -17
  83. package/lib/query/InsertClause.d.ts +0 -7
  84. package/lib/query/SelectClause.cjs +0 -20
  85. package/lib/query/SelectClause.d.ts +0 -7
  86. package/lib/query/ValuesClause.cjs +0 -16
  87. package/lib/query/ValuesClause.d.ts +0 -7
  88. package/lib/query/WhereClause.cjs +0 -61
  89. package/lib/query/WhereClause.d.ts +0 -7
  90. package/lib/query/factory.cjs +0 -121
  91. package/lib/query/factory.d.ts +0 -25
  92. package/lib/sequences/utils.cjs +0 -19
  93. package/lib/sequences/utils.d.ts +0 -1
package/README.md CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  # Decaf CouchDB Module
4
4
 
5
+ A TypeScript adapter for CouchDB database operations, providing a seamless integration with the Decaf.ts framework. This module offers a comprehensive set of tools for working with CouchDB databases, including support for Mango queries, document operations, sequence management, and indexing capabilities.
6
+
7
+
5
8
  ![Licence](https://img.shields.io/github/license/decaf-ts/for-couchdb.svg?style=plastic)
6
9
  ![GitHub language count](https://img.shields.io/github/languages/count/decaf-ts/for-couchdb?style=plastic)
7
10
  ![GitHub top language](https://img.shields.io/github/languages/top/decaf-ts/for-couchdb?style=plastic)
@@ -33,8 +36,35 @@ Documentation available [here](https://decaf-ts.github.io/for-couchdb/)
33
36
 
34
37
  ### Description
35
38
 
36
- A very versatile persistence layer. from smart contracts, Digital wallets or just regular database access
39
+ The Decaf CouchDB Module is a versatile persistence layer designed to provide seamless integration between the Decaf.ts framework and CouchDB databases. It offers a comprehensive set of tools and abstractions that simplify working with CouchDB's unique features while maintaining type safety and following best practices.
40
+
41
+ #### Core Components
42
+
43
+ 1. **CouchDBAdapter**: An abstract base class that provides the foundation for CouchDB database operations. It handles CRUD operations, sequence management, and error handling. Developers can extend this class to create custom adapters tailored to their specific needs.
44
+
45
+ 2. **Query System**: A powerful query builder with support for CouchDB's Mango queries:
46
+ - **CouchDBStatement**: Provides a fluent interface for building type-safe Mango queries
47
+ - **CouchDBPaginator**: Implements pagination for query results using CouchDB's bookmark system
48
+ - **Operator Translation**: Converts Decaf.ts core operators to CouchDB Mango operators
37
49
 
50
+ 3. **Indexing**: Tools for creating and managing CouchDB indexes:
51
+ - **Index Generation**: Automatically generates appropriate index configurations based on model metadata
52
+ - **Index Management**: Utilities for creating and maintaining indexes
53
+
54
+ 4. **Sequence Management**: A robust system for generating sequential IDs:
55
+ - **CouchDBSequence**: Implements the Sequence interface for CouchDB
56
+ - **Sequence Model**: Provides a data model for storing sequence information
57
+
58
+ 5. **Error Handling**: Specialized error types and utilities for handling CouchDB-specific errors:
59
+ - **Error Translation**: Converts CouchDB error codes and messages to appropriate Decaf.ts error types
60
+ - **IndexError**: Specialized error for index-related issues
61
+
62
+ 6. **Utilities**: Helper functions for common CouchDB operations:
63
+ - **Authentication**: Functions for handling CouchDB authentication
64
+ - **Connection Management**: Utilities for managing database connections
65
+ - **Document Processing**: Tools for processing CouchDB documents
66
+
67
+ This module serves as a bridge between your application and CouchDB, abstracting away the complexities of the database while providing a type-safe, consistent API that integrates seamlessly with the rest of the Decaf.ts ecosystem.
38
68
 
39
69
 
40
70
  ### How to Use
@@ -42,7 +72,347 @@ A very versatile persistence layer. from smart contracts, Digital wallets or jus
42
72
  - [Initial Setup](./tutorials/For%20Developers.md#_initial-setup_)
43
73
  - [Installation](./tutorials/For%20Developers.md#installation)
44
74
 
75
+ ## Creating a CouchDB Adapter
76
+
77
+ To use the CouchDB module, you need to create a concrete implementation of the CouchDBAdapter class:
78
+
79
+ ```typescript
80
+ import { CouchDBAdapter } from '@decaf-ts/for-couchdb';
81
+ import { Constructor, Model } from '@decaf-ts/decorator-validation';
82
+ import { MangoQuery } from '@decaf-ts/for-couchdb';
83
+ import { generateIndexes } from '@decaf-ts/for-couchdb';
84
+ import * as nano from 'nano';
85
+
86
+ // Define your scope, flags, and context types
87
+ interface MyScope {
88
+ config: {
89
+ couchdb: {
90
+ url: string;
91
+ username: string;
92
+ password: string;
93
+ database: string;
94
+ }
95
+ }
96
+ }
97
+
98
+ class MyCouchDBAdapter extends CouchDBAdapter<MyScope, MyFlags, MyContext> {
99
+ private db: any;
100
+
101
+ constructor(scope: MyScope) {
102
+ super(scope, 'my-couchdb', 'my-alias');
103
+
104
+ // Initialize connection to CouchDB
105
+ const { url, username, password, database } = scope.config.couchdb;
106
+ const connection = nano(url);
107
+ this.db = wrapDocumentScope(connection, database, username, password);
108
+ }
109
+
110
+ // Implement abstract methods
111
+ async index<M extends Model>(...models: Constructor<M>[]): Promise<void> {
112
+ const indexes = generateIndexes(models);
113
+ for (const index of indexes) {
114
+ try {
115
+ await this.db.createIndex(index);
116
+ } catch (error) {
117
+ throw this.parseError(error);
118
+ }
119
+ }
120
+ }
121
+
122
+ async raw<R>(rawInput: MangoQuery, docsOnly: boolean): Promise<R> {
123
+ try {
124
+ const result = await this.db.find(rawInput);
125
+ return docsOnly ? result.docs : result;
126
+ } catch (error) {
127
+ throw this.parseError(error);
128
+ }
129
+ }
130
+
131
+ async create(tableName: string, id: string | number, model: Record<string, any>, ...args: any[]): Promise<Record<string, any>> {
132
+ try {
133
+ const result = await this.db.insert(model);
134
+ return this.assignMetadata(model, result.rev);
135
+ } catch (error) {
136
+ throw this.parseError(error);
137
+ }
138
+ }
139
+
140
+ async read(tableName: string, id: string | number, ...args: any[]): Promise<Record<string, any>> {
141
+ try {
142
+ const docId = this.generateId(tableName, id);
143
+ const doc = await this.db.get(docId);
144
+ return this.assignMetadata(doc, doc._rev);
145
+ } catch (error) {
146
+ throw this.parseError(error);
147
+ }
148
+ }
149
+
150
+ async update(tableName: string, id: string | number, model: Record<string, any>, ...args: any[]): Promise<Record<string, any>> {
151
+ try {
152
+ const result = await this.db.insert(model);
153
+ return this.assignMetadata(model, result.rev);
154
+ } catch (error) {
155
+ throw this.parseError(error);
156
+ }
157
+ }
158
+
159
+ async delete(tableName: string, id: string | number, ...args: any[]): Promise<Record<string, any>> {
160
+ try {
161
+ const docId = this.generateId(tableName, id);
162
+ const doc = await this.db.get(docId);
163
+ const result = await this.db.destroy(docId, doc._rev);
164
+ return { id, _deleted: true };
165
+ } catch (error) {
166
+ throw this.parseError(error);
167
+ }
168
+ }
169
+ }
170
+ ```
171
+
172
+ ## Defining Models
173
+
174
+ Define your data models using the decorators from Decaf.ts:
175
+
176
+ ```typescript
177
+ import { model, required, validate } from '@decaf-ts/decorator-validation';
178
+ import { BaseModel, pk, index, table } from '@decaf-ts/core';
179
+
180
+ @table('users')
181
+ @model()
182
+ export class User extends BaseModel {
183
+ @pk()
184
+ id!: string;
185
+
186
+ @required()
187
+ @index()
188
+ email!: string;
189
+
190
+ @required()
191
+ firstName!: string;
192
+
193
+ @required()
194
+ lastName!: string;
195
+
196
+ @index()
197
+ age?: number;
198
+
199
+ constructor(data?: Partial<User>) {
200
+ super(data);
201
+ }
202
+ }
203
+ ```
204
+
205
+ ## Creating a Repository
206
+
207
+ Create a repository for your model:
208
+
209
+ ```typescript
210
+ import { Repository } from '@decaf-ts/core';
211
+ import { CouchDBRepository } from '@decaf-ts/for-couchdb';
212
+ import { User } from './models/User';
213
+ import { MyCouchDBAdapter } from './adapters/MyCouchDBAdapter';
214
+
215
+ // Get the adapter instance
216
+ const adapter = new MyCouchDBAdapter(myScope);
217
+
218
+ // Create a repository for the User model
219
+ const userRepository: CouchDBRepository<User, MyScope, MyFlags, MyContext> =
220
+ Repository.forModel(User, adapter.flavour);
221
+ ```
222
+
223
+ ## Basic CRUD Operations
224
+
225
+ Perform basic CRUD operations:
226
+
227
+ ```typescript
228
+ // Create a new user
229
+ const newUser = new User({
230
+ id: '123',
231
+ email: 'john.doe@example.com',
232
+ firstName: 'John',
233
+ lastName: 'Doe',
234
+ age: 30
235
+ });
236
+
237
+ // Create
238
+ const createdUser = await userRepository.create(newUser);
239
+
240
+ // Read
241
+ const user = await userRepository.read('123');
242
+
243
+ // Update
244
+ user.age = 31;
245
+ const updatedUser = await userRepository.update(user);
246
+
247
+ // Delete
248
+ await userRepository.delete('123');
249
+ ```
250
+
251
+ ## Using Statements for Queries
252
+
253
+ Build and execute queries using the Statement builder:
254
+
255
+ ```typescript
256
+ import { Condition } from '@decaf-ts/core';
257
+
258
+ // Create a statement
259
+ const statement = adapter.Statement<User>();
260
+
261
+ // Build a query to find users older than 25, sorted by lastName
262
+ const users = await statement
263
+ .from(User)
264
+ .where(Condition.attribute<User>('age').gt(25))
265
+ .orderBy('lastName', 'asc')
266
+ .limit(10)
267
+ .execute<User[]>();
268
+
269
+ // Query with multiple conditions
270
+ const johnDoes = await statement
271
+ .from(User)
272
+ .where(
273
+ Condition.and(
274
+ Condition.attribute<User>('lastName').eq('Doe'),
275
+ Condition.attribute<User>('age').gt(18)
276
+ )
277
+ )
278
+ .execute<User[]>();
279
+
280
+ // Select specific fields
281
+ const userEmails = await statement
282
+ .from(User)
283
+ .select(['email', 'firstName'])
284
+ .where(Condition.attribute<User>('age').gt(25))
285
+ .execute<Array<Pick<User, 'email' | 'firstName'>>>();
286
+ ```
287
+
288
+ ## Pagination
289
+
290
+ Paginate through query results:
291
+
292
+ ```typescript
293
+ // Create a paginator
294
+ const paginator = await adapter
295
+ .Statement<User>()
296
+ .from(User)
297
+ .where(Condition.attribute<User>('age').gt(18))
298
+ .orderBy('lastName', 'asc')
299
+ .paginate<User[]>(10); // 10 items per page
300
+
301
+ // Get the first page
302
+ const page1 = await paginator.page(1);
303
+
304
+ // Get the next page
305
+ const page2 = await paginator.page(2);
306
+ ```
307
+
308
+ ## Working with Sequences
309
+
310
+ Generate sequential IDs:
311
+
312
+ ```typescript
313
+ import { SequenceOptions } from '@decaf-ts/core';
314
+
315
+ // Create a sequence
316
+ const sequenceOptions: SequenceOptions = {
317
+ name: 'user-sequence',
318
+ startWith: 1000,
319
+ incrementBy: 1,
320
+ type: 'Number'
321
+ };
322
+
323
+ const sequence = await adapter.Sequence(sequenceOptions);
324
+
325
+ // Get the next value
326
+ const nextId = await sequence.next();
327
+
328
+ // Get a range of values
329
+ const idRange = await sequence.range(5); // Returns 5 sequential IDs
330
+ ```
331
+
332
+ ## Index Management
333
+
334
+ Create and manage indexes:
335
+
336
+ ```typescript
337
+ import { generateIndexDoc } from '@decaf-ts/for-couchdb';
338
+
339
+ // Generate an index configuration
340
+ const indexConfig = generateIndexDoc(
341
+ 'email', // attribute
342
+ 'users', // tableName
343
+ ['firstName'], // compositions
344
+ 'asc' // order
345
+ );
346
+
347
+ // Create the index
348
+ await adapter.db.createIndex(indexConfig);
349
+
350
+ // Initialize indexes for all models
351
+ await adapter.initialize();
352
+ ```
353
+
354
+ ## Error Handling
355
+
356
+ Handle CouchDB-specific errors:
357
+
358
+ ```typescript
359
+ import { IndexError, ConflictError, NotFoundError } from '@decaf-ts/for-couchdb';
360
+
361
+ try {
362
+ // Some operation that might fail
363
+ await userRepository.read('non-existent-id');
364
+ } catch (error) {
365
+ if (error instanceof NotFoundError) {
366
+ console.error('Document not found:', error.message);
367
+ } else if (error instanceof ConflictError) {
368
+ console.error('Document conflict:', error.message);
369
+ } else if (error instanceof IndexError) {
370
+ console.error('Index error:', error.message);
371
+ } else {
372
+ console.error('Unexpected error:', error);
373
+ }
374
+ }
375
+ ```
376
+
377
+ ## Raw Mango Queries
378
+
379
+ Execute raw Mango queries:
380
+
381
+ ```typescript
382
+ import { MangoQuery } from '@decaf-ts/for-couchdb';
383
+
384
+ // Define a raw Mango query
385
+ const rawQuery: MangoQuery = {
386
+ selector: {
387
+ '??table': 'users',
388
+ age: { $gt: 25 },
389
+ lastName: { $eq: 'Doe' }
390
+ },
391
+ fields: ['_id', 'firstName', 'lastName', 'email'],
392
+ sort: [{ lastName: 'asc' }],
393
+ limit: 20
394
+ };
395
+
396
+ // Execute the raw query
397
+ const results = await adapter.raw(rawQuery, true);
398
+ ```
399
+
400
+ ## Utility Functions
401
+
402
+ Use utility functions for common operations:
403
+
404
+ ```typescript
405
+ import { reAuth, wrapDocumentScope, generateIndexName } from '@decaf-ts/for-couchdb';
406
+
407
+ // Re-authenticate a connection
408
+ await reAuth(connection, 'username', 'password');
45
409
 
410
+ // Wrap a document scope with automatic re-authentication
411
+ const db = wrapDocumentScope(connection, 'my-database', 'username', 'password');
412
+
413
+ // Generate an index name
414
+ const indexName = generateIndexName('email', 'users', ['firstName'], 'asc');
415
+ ```
46
416
 
47
417
 
48
418
  ### Related