@docstack/client 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.
Files changed (77) hide show
  1. package/README.md +214 -0
  2. package/lib/core/attribute.d.ts +174 -0
  3. package/lib/core/attribute.js +296 -0
  4. package/lib/core/attribute.js.map +1 -0
  5. package/lib/core/class.d.ts +340 -0
  6. package/lib/core/class.js +540 -0
  7. package/lib/core/class.js.map +1 -0
  8. package/lib/core/crypto-engine/index.d.ts +121 -0
  9. package/lib/core/crypto-engine/index.js +130 -0
  10. package/lib/core/crypto-engine/index.js.map +1 -0
  11. package/lib/core/crypto-engine/utils.d.ts +20 -0
  12. package/lib/core/crypto-engine/utils.js +76 -0
  13. package/lib/core/crypto-engine/utils.js.map +1 -0
  14. package/lib/core/datamodel/index.d.ts +3 -0
  15. package/lib/core/datamodel/index.js +1186 -0
  16. package/lib/core/datamodel/index.js.map +1 -0
  17. package/lib/core/domain.d.ts +200 -0
  18. package/lib/core/domain.js +285 -0
  19. package/lib/core/domain.js.map +1 -0
  20. package/lib/core/index.d.ts +176 -0
  21. package/lib/core/index.js +379 -0
  22. package/lib/core/index.js.map +1 -0
  23. package/lib/core/job-engine/index.d.ts +110 -0
  24. package/lib/core/job-engine/index.js +116 -0
  25. package/lib/core/job-engine/index.js.map +1 -0
  26. package/lib/core/policy-engine/index.d.ts +97 -0
  27. package/lib/core/policy-engine/index.js +150 -0
  28. package/lib/core/policy-engine/index.js.map +1 -0
  29. package/lib/core/query-engine/accumulators.d.ts +9 -0
  30. package/lib/core/query-engine/accumulators.js +258 -0
  31. package/lib/core/query-engine/accumulators.js.map +1 -0
  32. package/lib/core/query-engine/evaluator.d.ts +37 -0
  33. package/lib/core/query-engine/evaluator.js +179 -0
  34. package/lib/core/query-engine/evaluator.js.map +1 -0
  35. package/lib/core/query-engine/executor.d.ts +14 -0
  36. package/lib/core/query-engine/executor.js +405 -0
  37. package/lib/core/query-engine/executor.js.map +1 -0
  38. package/lib/core/query-engine/index.d.ts +4 -0
  39. package/lib/core/query-engine/index.js +4 -0
  40. package/lib/core/query-engine/index.js.map +1 -0
  41. package/lib/core/query-engine/parser.d.ts +10 -0
  42. package/lib/core/query-engine/parser.js +515 -0
  43. package/lib/core/query-engine/parser.js.map +1 -0
  44. package/lib/core/query-engine/planner.d.ts +27 -0
  45. package/lib/core/query-engine/planner.js +330 -0
  46. package/lib/core/query-engine/planner.js.map +1 -0
  47. package/lib/core/stack.d.ts +497 -0
  48. package/lib/core/stack.js +1507 -0
  49. package/lib/core/stack.js.map +1 -0
  50. package/lib/core/test-utils/docstack.d.ts +29 -0
  51. package/lib/core/test-utils/docstack.js +222 -0
  52. package/lib/core/test-utils/docstack.js.map +1 -0
  53. package/lib/core/trigger/index.d.ts +31 -0
  54. package/lib/core/trigger/index.js +81 -0
  55. package/lib/core/trigger/index.js.map +1 -0
  56. package/lib/index.d.ts +4 -0
  57. package/lib/index.js +8237 -0
  58. package/lib/index.js.map +1 -0
  59. package/lib/plugins/pouchdb.d.ts +9 -0
  60. package/lib/plugins/pouchdb.js +403 -0
  61. package/lib/plugins/pouchdb.js.map +1 -0
  62. package/lib/utils/crypto/index.d.ts +3 -0
  63. package/lib/utils/crypto/index.js +34 -0
  64. package/lib/utils/crypto/index.js.map +1 -0
  65. package/lib/utils/index.d.ts +4 -0
  66. package/lib/utils/index.js +58 -0
  67. package/lib/utils/index.js.map +1 -0
  68. package/lib/utils/logger/index.d.ts +4 -0
  69. package/lib/utils/logger/index.js +20 -0
  70. package/lib/utils/logger/index.js.map +1 -0
  71. package/lib/utils/logger/transport.d.ts +11 -0
  72. package/lib/utils/logger/transport.js +28 -0
  73. package/lib/utils/logger/transport.js.map +1 -0
  74. package/lib/workers/dataModel.d.ts +1 -0
  75. package/lib/workers/dataModel.js +48 -0
  76. package/lib/workers/dataModel.js.map +1 -0
  77. package/package.json +60 -0
@@ -0,0 +1,497 @@
1
+ import Class from "./class.js";
2
+ import Domain from "./domain.js";
3
+ import { Stack, StackOptions, AuthSessionProof, ClientCredentials, CachedClass, ClassModelPropagationStart, ClassModelPropagationComplete, CachedDomain, DomainModel } from "@docstack/shared";
4
+ import { SystemDoc, Patch, ClassModel, Document, RelationDocument } from "@docstack/shared";
5
+ import type { SelectAST, UnionAST } from "./query-engine/index.js";
6
+ import { JobEngine } from "./job-engine/index.js";
7
+ import { PolicyEngine } from "./policy-engine/index.js";
8
+ import { CryptoEngine } from "./crypto-engine/index.js";
9
+ export declare const BASE_SCHEMA: ClassModel["schema"];
10
+ export declare const CLASS_SCHEMA: ClassModel["schema"];
11
+ /**
12
+ * The core database engine for DocStack client applications.
13
+ *
14
+ * ClientStack provides a complete offline-first datastore built on PouchDB with:
15
+ * - Schema validation and class-based document modeling
16
+ * - SQL-like querying capabilities
17
+ * - Field-level encryption via {@link CryptoEngine}
18
+ * - Access control via {@link PolicyEngine}
19
+ * - Background job execution via {@link JobEngine}
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * // Create a new stack instance
24
+ * const stack = await ClientStack.create('my-app-db');
25
+ *
26
+ * // Authenticate a user
27
+ * const session = await stack.authenticate({ username: 'admin', password: 'secret' });
28
+ *
29
+ * // Query documents using SQL
30
+ * const { rows } = await stack.query('SELECT * FROM Task WHERE isComplete = false');
31
+ * ```
32
+ *
33
+ * @extends Stack
34
+ */
35
+ declare class ClientStack extends Stack {
36
+ private static readonly CRYPTO_CONFIG_DOC_ID;
37
+ /**
38
+ * The underlying PouchDB database instance.
39
+ * Initialized asynchronously during stack creation.
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * // Access the raw PouchDB API for advanced operations
44
+ * const allDocs = await stack.db.allDocs({ include_docs: true });
45
+ * ```
46
+ */
47
+ db: PouchDB.Database<{}>;
48
+ /** The unique name identifier for this stack instance, derived from the connection string. */
49
+ name: string;
50
+ lastDocId: number;
51
+ /** The connection string used to create this stack. */
52
+ connection: string;
53
+ /** Configuration options provided during stack creation. */
54
+ options?: StackOptions;
55
+ /** The current application version string. */
56
+ appVersion: string;
57
+ /**
58
+ * In-memory cache for Class and Domain objects.
59
+ * Items are cached with a 15-minute TTL to improve performance.
60
+ */
61
+ cache: {
62
+ [className: string]: CachedClass | CachedDomain;
63
+ };
64
+ patchCount: number;
65
+ listeners: PouchDB.Core.Changes<{}>[];
66
+ modelWorker: Worker | null;
67
+ /**
68
+ * Engine for executing background jobs and scheduled tasks.
69
+ * Jobs are defined as documents and run in a sandboxed environment.
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * const run = await stack.jobEngine.executeJob('Job-CleanupOldData');
74
+ * console.log('Job completed:', run.status);
75
+ * ```
76
+ */
77
+ jobEngine: JobEngine;
78
+ /**
79
+ * Engine for enforcing read/write access control policies.
80
+ * Policies are evaluated based on user session and document content.
81
+ */
82
+ policyEngine: PolicyEngine;
83
+ /**
84
+ * Engine for field-level encryption and decryption.
85
+ * Handles key derivation (PBKDF2) and AES-GCM encryption.
86
+ */
87
+ cryptoEngine: CryptoEngine;
88
+ schemaVersion: string | undefined;
89
+ /**
90
+ * The current authenticated user session, if any.
91
+ * Contains session details, derived key, and document encryption key.
92
+ */
93
+ authSession?: AuthSessionProof;
94
+ private cryptoEngineDisabled;
95
+ private constructor();
96
+ private initialize;
97
+ /**
98
+ * Returns the underlying PouchDB database instance.
99
+ * @returns The PouchDB database
100
+ */
101
+ getDb(): PouchDB.Database<{}>;
102
+ /**
103
+ * Retrieves information about the database including document count and update sequence.
104
+ * @returns Database information object
105
+ */
106
+ getDbInfo(): Promise<PouchDB.Core.DatabaseInfo>;
107
+ /**
108
+ * Returns the name of the underlying PouchDB database.
109
+ * @returns The database name string
110
+ */
111
+ getDbName(): string;
112
+ /**
113
+ * Checks if the crypto engine was disabled during stack initialization.
114
+ * @returns `true` if encryption is disabled, `false` otherwise
115
+ */
116
+ isCryptoEngineDisabled(): boolean;
117
+ /**
118
+ * Sets the current authentication session.
119
+ * Called automatically by {@link authenticate}, but can be set manually for custom auth flows.
120
+ * @param proof - The authentication session proof containing session and encryption keys
121
+ */
122
+ setAuthSession(proof: AuthSessionProof): void;
123
+ /**
124
+ * Clears the current authentication session and removes the document encryption key.
125
+ * Call this when a user logs out.
126
+ */
127
+ clearAuthSession(): void;
128
+ /**
129
+ * Exports all documents from the database.
130
+ * Useful for debugging or creating backups.
131
+ * @returns All documents including their content
132
+ */
133
+ dump: () => Promise<PouchDB.Core.AllDocsResponse<{}>>;
134
+ private ensureDefaultPolicyForClass;
135
+ /**
136
+ * Creates and initializes a new ClientStack instance.
137
+ * This is the primary way to instantiate a stack - the constructor is private.
138
+ *
139
+ * @param conn - The connection string or database name
140
+ * @param options - Optional configuration including plugins, patches, and credentials
141
+ * @returns A fully initialized ClientStack instance
142
+ *
143
+ * @example
144
+ * ```typescript
145
+ * // Basic initialization
146
+ * const stack = await ClientStack.create('my-app-db');
147
+ *
148
+ * // With authentication
149
+ * const stack = await ClientStack.create('my-app-db', {
150
+ * credentials: { username: 'admin', password: 'secret' }
151
+ * });
152
+ *
153
+ * // With custom patches
154
+ * const stack = await ClientStack.create('my-app-db', {
155
+ * patches: [myCustomPatch]
156
+ * });
157
+ * ```
158
+ */
159
+ static create(conn: string, options?: StackOptions): Promise<ClientStack>;
160
+ /**
161
+ * Authenticates a user and establishes a session.
162
+ *
163
+ * This method:
164
+ * 1. Looks up the user by username
165
+ * 2. Executes the configured authentication job (e.g., password verification)
166
+ * 3. Creates a new session document
167
+ * 4. Sets up encryption keys for the session
168
+ *
169
+ * @param credentials - The user's login credentials containing username and password
170
+ * @returns The authentication session proof containing session info and encryption keys
171
+ * @throws Error if the user is not found or authentication fails
172
+ *
173
+ * @example
174
+ * ```typescript
175
+ * const proof = await stack.authenticate({
176
+ * username: 'john.doe',
177
+ * password: 'securePassword123'
178
+ * });
179
+ * console.log('Logged in as:', proof.session.username);
180
+ * ```
181
+ */
182
+ authenticate(credentials: ClientCredentials): Promise<AuthSessionProof>;
183
+ getLastDocId(): Promise<number>;
184
+ getSystem(): Promise<SystemDoc>;
185
+ private loadPatches;
186
+ applyPatch: (patch: Patch) => Promise<string>;
187
+ private applyPatches;
188
+ checkSystem(): Promise<void>;
189
+ setListeners: () => void;
190
+ /**
191
+ * @description Clears all listeners from the Stack
192
+ */
193
+ removeAllListeners: () => void;
194
+ /**
195
+ * @description When a class model propagation starts write the ~lock document to the database.
196
+ * It prevents any further modifications on the class data model
197
+ * @param event
198
+ */
199
+ onClassModelPropagationStart: (event: CustomEvent<ClassModelPropagationStart>) => void;
200
+ /**
201
+ * @description When a class model propagation comes to completion remove the corresponding
202
+ * ~lock from the database
203
+ * @param event
204
+ */
205
+ onClassModelPropagationComplete: (event: CustomEvent<ClassModelPropagationComplete>) => void;
206
+ /**
207
+ * @returns PouchDB.Core.Changes<{}>
208
+ */
209
+ onClassModelChanges: () => PouchDB.Core.Changes<{}>;
210
+ onClassLock: (className: string) => PouchDB.Core.Changes<{}>;
211
+ addClassLock: (className: string) => Promise<boolean>;
212
+ clearClassLock: (className: string) => Promise<boolean>;
213
+ onClassDoc: (className: string) => PouchDB.Core.Changes<{}>;
214
+ initdb(): Promise<this>;
215
+ private ensureCryptoConfigDocument;
216
+ private ensureCryptoMarkerEncryption;
217
+ private validateCryptoConfig;
218
+ /**
219
+ * Closes the stack and cleans up all resources.
220
+ * Removes event listeners and terminates background workers.
221
+ */
222
+ close: () => void;
223
+ /**
224
+ * Retrieves a Class instance by name.
225
+ * Results are cached for 15 minutes to improve performance.
226
+ *
227
+ * @param className - The name or ID of the class to retrieve
228
+ * @param fresh - If `true`, bypasses the cache and fetches from database
229
+ * @returns The Class instance, or `null` if not found
230
+ *
231
+ * @example
232
+ * ```typescript
233
+ * const taskClass = await stack.getClass('Task');
234
+ * if (taskClass) {
235
+ * const tasks = await taskClass.getCards();
236
+ * }
237
+ * ```
238
+ */
239
+ getClass: (className: string, fresh?: boolean) => Promise<Class | null>;
240
+ /**
241
+ * Retrieves a Domain instance by name.
242
+ * Results are cached for 15 minutes to improve performance.
243
+ *
244
+ * @param domainName - The name or ID of the domain to retrieve
245
+ * @param fresh - If `true`, bypasses the cache and fetches from database
246
+ * @returns The Domain instance, or `null` if not found
247
+ */
248
+ getDomain: (domainName: string, fresh?: boolean) => Promise<Domain | null>;
249
+ initIndex(): Promise<void>;
250
+ /**
251
+ * Retrieves a single document by its ID.
252
+ *
253
+ * @typeParam T - The expected document type
254
+ * @param docId - The document ID to retrieve
255
+ * @returns The document, or `null` if not found
256
+ *
257
+ * @example
258
+ * ```typescript
259
+ * const task = await stack.getDocument<TaskDocument>('Task-123');
260
+ * if (task) {
261
+ * console.log(task.title);
262
+ * }
263
+ * ```
264
+ */
265
+ getDocument<T extends Document>(docId: string): Promise<PouchDB.Core.ExistingDocument<T>>;
266
+ getDocRevision(docId: string): Promise<string>;
267
+ /**
268
+ * Finds multiple documents matching a PouchDB/Mango-style selector.
269
+ * Automatically filters to only active documents and applies access policies.
270
+ *
271
+ * @typeParam T - The expected document type
272
+ * @param selector - A PouchDB/Mango query selector
273
+ * @param fields - Optional list of fields to return
274
+ * @param skip - Number of documents to skip (for pagination)
275
+ * @param limit - Maximum number of documents to return
276
+ * @returns Object containing matching documents array
277
+ *
278
+ * @example
279
+ * ```typescript
280
+ * const result = await stack.findDocuments({
281
+ * '~class': { $eq: 'Task' },
282
+ * isComplete: { $eq: false }
283
+ * });
284
+ * console.log('Found tasks:', result.docs.length);
285
+ * ```
286
+ */
287
+ findDocuments: <T extends Document | RelationDocument = Document>(selector: {
288
+ [key: string]: any;
289
+ }, fields?: string[], skip?: number, limit?: number) => Promise<{
290
+ [key: string]: any;
291
+ docs: T[];
292
+ }>;
293
+ private processReadableDocument;
294
+ /**
295
+ * Finds a single document matching a selector.
296
+ * Convenience wrapper around {@link findDocuments} that returns the first match.
297
+ *
298
+ * @typeParam T - The expected document type
299
+ * @param selector - A PouchDB/Mango query selector
300
+ * @param fields - Optional list of fields to return
301
+ * @param skip - Number of documents to skip
302
+ * @param limit - Maximum number of documents to check
303
+ * @returns The first matching document, or `null` if none found
304
+ */
305
+ findDocument<T extends Document | RelationDocument = Document>(selector: any, fields?: any, skip?: any, limit?: any): Promise<T>;
306
+ getClassModel: (className: string) => Promise<ClassModel>;
307
+ getDomainModel: (domainName: string) => Promise<DomainModel>;
308
+ getClassModels: (conf?: {
309
+ listen?: boolean;
310
+ filter?: string[];
311
+ search?: string;
312
+ }) => Promise<{
313
+ list: ClassModel[];
314
+ listener?: undefined;
315
+ } | {
316
+ list: ClassModel[];
317
+ listener: PouchDB.Core.Changes<{}>;
318
+ }>;
319
+ getClasses: (conf: {
320
+ filter?: string[];
321
+ search?: string;
322
+ }) => Promise<Class[]>;
323
+ getDomainModels: (conf?: {
324
+ listen?: boolean;
325
+ filter?: string[];
326
+ search?: string;
327
+ }) => Promise<{
328
+ list: DomainModel[];
329
+ listener?: undefined;
330
+ } | {
331
+ list: DomainModel[];
332
+ listener: PouchDB.Core.Changes<{}>;
333
+ }>;
334
+ getDomains: (conf: {
335
+ filter?: string[];
336
+ search?: string;
337
+ }) => Promise<Domain[]>;
338
+ incrementLastDocId(): Promise<number>;
339
+ reset(): Promise<this>;
340
+ destroyDb(): Promise<boolean>;
341
+ static clear(conn: string): Promise<unknown>;
342
+ addClass: (classObj: Class) => Promise<ClassModel>;
343
+ addDomain: (domainObj: Domain) => Promise<DomainModel>;
344
+ updateClass: (classObj: Class) => Promise<Document>;
345
+ addDesignDocumentPKs: (className: string, pKs: string[], temp?: boolean) => Promise<string>;
346
+ prepareDoc(_id: string, type: string, params: {
347
+ [key: string]: string | number | boolean;
348
+ }, metaKey: "~class"): Document;
349
+ prepareDoc(_id: string, type: string, params: {
350
+ [key: string]: string | number | boolean;
351
+ }, metaKey: "~domain"): RelationDocument;
352
+ /**
353
+ * Creates or updates a single document in the database.
354
+ *
355
+ * If `docId` is provided and the document exists, it will be updated.
356
+ * If `docId` is `null`, a new ID will be auto-generated in the format `{type}-{incrementalId}`.
357
+ * Access policies are enforced before writing.
358
+ *
359
+ * @param docId - The document ID, or `null` to auto-generate
360
+ * @param type - The class name (e.g., 'Task', 'User')
361
+ * @param classObj - The Class instance or schema definition for validation
362
+ * @param params - The document data to save
363
+ * @returns The created or updated document
364
+ * @throws Error if policy check fails or document type conflicts
365
+ *
366
+ * @example
367
+ * ```typescript
368
+ * // Create with auto-generated ID
369
+ * const task = await stack.createDoc(null, 'Task', taskClass, {
370
+ * title: 'New Task',
371
+ * isComplete: false
372
+ * });
373
+ *
374
+ * // Update existing document
375
+ * await stack.createDoc('Task-123', 'Task', taskClass, {
376
+ * title: 'Updated Title'
377
+ * });
378
+ * ```
379
+ */
380
+ createDoc: (docId: string | null, type: string, classObj: Class | ClassModel["schema"], params: {}) => Promise<Document>;
381
+ /**
382
+ * Creates or updates multiple documents in a single batch operation.
383
+ * More efficient than calling {@link createDoc} multiple times.
384
+ *
385
+ * @param docs - Array of document specifications with optional docId and params
386
+ * @param type - The class name for all documents
387
+ * @param classObj - The Class instance or schema definition for validation
388
+ * @returns Array of created or updated documents
389
+ * @throws Error if policy check fails for any document
390
+ *
391
+ * @example
392
+ * ```typescript
393
+ * const tasks = await stack.createDocs([
394
+ * { docId: null, params: { title: 'Task 1' } },
395
+ * { docId: null, params: { title: 'Task 2' } },
396
+ * { docId: 'Task-existing', params: { title: 'Updated' } }
397
+ * ], 'Task', taskClass);
398
+ * ```
399
+ */
400
+ createDocs: (docs: {
401
+ docId: string | null;
402
+ params: {};
403
+ }[], type: string, classObj: Class | ClassModel["schema"]) => Promise<Document[]>;
404
+ /**
405
+ * Creates a relation document linking two entities via a Domain.
406
+ * Relation documents represent relationships between documents (e.g., 1:N, N:N).
407
+ *
408
+ * @param docId - The relation document ID, or `null` to auto-generate
409
+ * @param relationName - A descriptive name for this relation instance
410
+ * @param domainObj - The Domain defining the relationship type
411
+ * @param params - The relation parameters including source and target references
412
+ * @returns The created relation document, or `null` on error
413
+ *
414
+ * @example
415
+ * ```typescript
416
+ * const relation = await stack.createRelationDoc(
417
+ * null,
418
+ * 'ProjectTask',
419
+ * projectTaskDomain,
420
+ * {
421
+ * sourceClass: 'Project',
422
+ * targetClass: 'Task',
423
+ * sourceId: 'Project-1',
424
+ * targetId: 'Task-42'
425
+ * }
426
+ * );
427
+ * ```
428
+ */
429
+ createRelationDoc: (docId: string | null, relationName: string, domainObj: Domain, params: {
430
+ sourceClass: string;
431
+ targetClass: string;
432
+ sourceId: string;
433
+ targetId: string;
434
+ }) => Promise<RelationDocument | null>;
435
+ /**
436
+ * Creates multiple relation documents in a single batch operation.
437
+ * More efficient than calling {@link createRelationDoc} multiple times.
438
+ *
439
+ * @param docs - Array of relation specifications
440
+ * @param relationName - A descriptive name for these relations
441
+ * @param domainObj - The Domain defining the relationship type
442
+ * @returns Array of created relation documents
443
+ *
444
+ * @example
445
+ * ```typescript
446
+ * const relations = await stack.createRelationDocs([
447
+ * { docId: null, params: { sourceClass: 'Project', targetClass: 'Task', sourceId: 'Project-1', targetId: 'Task-1' } },
448
+ * { docId: null, params: { sourceClass: 'Project', targetClass: 'Task', sourceId: 'Project-1', targetId: 'Task-2' } }
449
+ * ], 'ProjectTasks', projectTaskDomain);
450
+ * ```
451
+ */
452
+ createRelationDocs: (docs: {
453
+ docId: string | null;
454
+ params: {
455
+ sourceClass: string;
456
+ targetClass: string;
457
+ sourceId: string;
458
+ targetId: string;
459
+ };
460
+ }[], relationName: string, domainObj: Domain) => Promise<RelationDocument[]>;
461
+ /**
462
+ * Sets the active param of a document to false
463
+ * @param _id
464
+ * @returns Promise<boolean>
465
+ */
466
+ deleteDocument: (_id: string) => Promise<boolean>;
467
+ /**
468
+ * Executes a SQL query against the local database.
469
+ * Supports SELECT, JOIN, WHERE, ORDER BY, GROUP BY, and UNION operations.
470
+ *
471
+ * @param sql - The SQL query string
472
+ * @param params - Optional query parameters for prepared statements
473
+ * @returns Object containing result rows and the parsed AST
474
+ *
475
+ * @example
476
+ * ```typescript
477
+ * // Simple select
478
+ * const { rows } = await stack.query('SELECT * FROM Task WHERE isComplete = false');
479
+ *
480
+ * // Join with ordering
481
+ * const { rows } = await stack.query(`
482
+ * SELECT t.title, u.username AS assignee
483
+ * FROM Task AS t
484
+ * JOIN User AS u ON u._id = t.assigneeId
485
+ * ORDER BY t.createdAt DESC
486
+ * `);
487
+ *
488
+ * // With parameters
489
+ * const { rows } = await stack.query('SELECT * FROM Task WHERE priority = ?', 'high');
490
+ * ```
491
+ */
492
+ query: (sql: string, ...params: any[]) => Promise<{
493
+ rows: any;
494
+ ast: (SelectAST | UnionAST)[];
495
+ }>;
496
+ }
497
+ export default ClientStack;