mango-orm 1.0.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.
@@ -0,0 +1,713 @@
1
+ /*
2
+ Requires sql for connection
3
+ */
4
+ import * as sql from "mysql";
5
+ /**
6
+ * MangoType - Schema builder for defining table column types
7
+ * Provides chainable methods to build SQL column definitions
8
+ *
9
+ * @example
10
+ * const type = new MangoType();
11
+ * type.varchar(255).notNull().unique();
12
+ */
13
+ class MangoType {
14
+ constructor() {
15
+ this.query = "";
16
+ }
17
+ /** Define an INT column */
18
+ int() {
19
+ this.query += " INT ";
20
+ return this;
21
+ }
22
+ /** Define a BIGINT column */
23
+ bigInt() {
24
+ this.query += " BIGINT ";
25
+ return this;
26
+ }
27
+ /** Define a FLOAT column */
28
+ float() {
29
+ this.query += " FLOAT ";
30
+ return this;
31
+ }
32
+ /**
33
+ * Define a CHAR column with fixed length
34
+ * @param length - The fixed character length
35
+ */
36
+ char(length) {
37
+ this.query += ` CHAR(${length}) `;
38
+ return this;
39
+ }
40
+ /** Define a TEXT column for large text data */
41
+ text() {
42
+ this.query += " TEXT ";
43
+ return this;
44
+ }
45
+ /** Define a DATE column (YYYY-MM-DD) */
46
+ date() {
47
+ this.query += " DATE ";
48
+ return this;
49
+ }
50
+ /** Define a DATETIME column (YYYY-MM-DD HH:MM:SS) */
51
+ dateTime() {
52
+ this.query += " DATETIME ";
53
+ return this;
54
+ }
55
+ /** Define a TIMESTAMP column (auto-updated on changes) */
56
+ timeStamp() {
57
+ this.query += " TIMESTAMP ";
58
+ return this;
59
+ }
60
+ /** Define a BOOLEAN column (stored as TINYINT(1)) */
61
+ boolean() {
62
+ this.query += " BOOLEAN ";
63
+ return this;
64
+ }
65
+ /**
66
+ * Define a TINYINT column
67
+ * @param length - Display width (e.g., TINYINT(1))
68
+ */
69
+ tinyInt(length) {
70
+ this.query += ` TINYINT(${length})`;
71
+ return this;
72
+ }
73
+ /** Make column auto-incrementing (use with INT/BIGINT) */
74
+ autoIncrement() {
75
+ this.query += " AUTO_INCREMENT ";
76
+ return this;
77
+ }
78
+ /** Mark column as primary key */
79
+ primaryKey() {
80
+ this.query += " PRIMARY KEY ";
81
+ return this;
82
+ }
83
+ /**
84
+ * Define a VARCHAR column with variable length
85
+ * @param length - Maximum character length (e.g., 255)
86
+ */
87
+ varchar(length) {
88
+ this.query += ` VARCHAR(${length}) `;
89
+ return this;
90
+ }
91
+ /** Make column NOT NULL (required field) */
92
+ notNull() {
93
+ this.query += ` NOT NULL `;
94
+ return this;
95
+ }
96
+ /** Add UNIQUE constraint (no duplicate values) */
97
+ unique() {
98
+ this.query += " UNIQUE ";
99
+ return this;
100
+ }
101
+ /** Get the built SQL column definition */
102
+ getQuery() {
103
+ return this.query;
104
+ }
105
+ }
106
+ /**
107
+ * MangoQuery - Internal query executor
108
+ * Handles SQL query execution and prepared statements
109
+ * Automatically resets state after execution to prevent query contamination
110
+ */
111
+ class MangoQuery {
112
+ constructor() {
113
+ this.query = "";
114
+ this.supplies = []; // Prepared statement parameters
115
+ }
116
+ /**
117
+ * Configure the database connection pool
118
+ * @param db - MySQL connection pool instance
119
+ */
120
+ config(db) {
121
+ this.db = db;
122
+ }
123
+ /**
124
+ * Execute the built query with prepared statements
125
+ * Automatically resets query and supplies after execution
126
+ * @returns Promise resolving to query results
127
+ */
128
+ execute() {
129
+ return new Promise((resolve, reject) => {
130
+ this.db.query(this.query, this.supplies, (err, result) => {
131
+ // Reset state BEFORE resolving to prevent contamination
132
+ this.query = "";
133
+ this.supplies = [];
134
+ if (err)
135
+ reject(err);
136
+ else
137
+ resolve(result);
138
+ });
139
+ });
140
+ }
141
+ /**
142
+ * Execute a custom SQL query with parameters
143
+ * Use for complex queries not covered by the query builder
144
+ * @param query - Raw SQL query string
145
+ * @param supplies - Array of parameter values
146
+ * @returns Promise resolving to query results
147
+ */
148
+ customQuery(query, supplies) {
149
+ return new Promise((resolve, reject) => {
150
+ this.db.query(query, supplies, (err, result) => {
151
+ if (err)
152
+ reject(err);
153
+ else
154
+ resolve(result);
155
+ });
156
+ });
157
+ }
158
+ }
159
+ /**
160
+ * MangoTable<T> - Main query builder class for table operations
161
+ * Provides chainable methods for building SQL queries
162
+ * Generic type T represents the shape of table rows
163
+ *
164
+ * @example
165
+ * const users = new MangoTable(db, 'users', ['id', 'name', 'email']);
166
+ * const result = await users.selectAll().where('id', '=', 1).execute();
167
+ */
168
+ class MangoTable {
169
+ /**
170
+ * Create a new table instance
171
+ * @param db - MySQL connection pool
172
+ * @param name - Table name (no spaces allowed)
173
+ * @param fields - Array of column names in the table
174
+ */
175
+ constructor(db, name, fields = []) {
176
+ this.query = new MangoQuery();
177
+ // Validate that fields are provided
178
+ if (fields.length == 0 || (fields.length == 1 && fields[0] === "")) {
179
+ throw new Error("no fields provided for table " + name);
180
+ }
181
+ this.db = db;
182
+ // Validate table name doesn't contain spaces
183
+ if (Array.from(name.split(" ")).length > 1) {
184
+ throw new Error("No spaces in table name allowed:");
185
+ }
186
+ this.tableName = name;
187
+ this.tableFields = [...fields]; // Clone to prevent external mutations
188
+ // Initialize query executor
189
+ // this.query = new MangoQuery();
190
+ this.query.config(db);
191
+ }
192
+ /**
193
+ * Add new columns to an existing table
194
+ * Updates internal field list for validation
195
+ * @param fields - Object mapping column names to MangoType definitions
196
+ * @returns this for method chaining
197
+ *
198
+ * @example
199
+ * table.addColumns({
200
+ * age: mango.types().int().notNull(),
201
+ * status: mango.types().varchar(50)
202
+ * }).execute();
203
+ */
204
+ addColumns(fields) {
205
+ // Early return if no fields provided
206
+ if (Object.entries(fields).length === 0)
207
+ return this;
208
+ this.query.query += "ALTER TABLE " + this.tableName + "\n";
209
+ const entries = Object.entries(fields);
210
+ this.query.query += entries
211
+ .map(([key, value]) => {
212
+ let QUERY = " ADD COLUMN ";
213
+ // Validate column name doesn't contain spaces
214
+ if (Array.from(key.split(" ")).length > 1) {
215
+ throw new Error("Field/Column name cannot have spaces: " +
216
+ key +
217
+ " from table : " +
218
+ this.tableName);
219
+ }
220
+ QUERY += key + " ";
221
+ QUERY += " " + value.getQuery();
222
+ return QUERY;
223
+ })
224
+ .join(",\n");
225
+ this.query.query += ";\n";
226
+ // Update internal field list for future validations
227
+ entries.forEach(([key]) => this.tableFields.push(key));
228
+ return this;
229
+ }
230
+ /**
231
+ * Remove columns from the table
232
+ * Validates columns exist before removal
233
+ * @param fields - Array of column names to remove
234
+ * @returns this for method chaining
235
+ *
236
+ * @example
237
+ * table.removeColumns(['old_field', 'deprecated_col']).execute();
238
+ */
239
+ removeColumns(fields) {
240
+ // Early return if no fields provided
241
+ if (fields.length === 0)
242
+ return this;
243
+ this.query.query += "ALTER TABLE " + this.tableName + "\n";
244
+ this.query.query += fields
245
+ .map((field) => {
246
+ let QUERY = " DROP COLUMN ";
247
+ // Validate field exists in table
248
+ if (!this.tableFields.includes(field)) {
249
+ throw new Error("field/column : " +
250
+ field +
251
+ " does not exist in table : " +
252
+ this.tableName);
253
+ }
254
+ QUERY += field;
255
+ return QUERY;
256
+ })
257
+ .join(",\n");
258
+ this.query.query += ";\n";
259
+ // Remove fields from internal tracking
260
+ this.tableFields = this.tableFields.filter((value) => !fields.includes(value));
261
+ return this;
262
+ }
263
+ /**
264
+ * Select all columns from the table
265
+ * @returns this for method chaining
266
+ *
267
+ * @example
268
+ * await table.selectAll().where('active', '=', true).execute();
269
+ */
270
+ selectAll() {
271
+ this.query.query = `SELECT * from ${this.tableName}`;
272
+ return this;
273
+ }
274
+ /**
275
+ * Select specific columns from the table
276
+ * Validates all columns exist before building query
277
+ * @param columns - Array of column names to select
278
+ * @returns this for method chaining
279
+ *
280
+ * @example
281
+ * await table.selectColumns(['id', 'name', 'email']).execute();
282
+ */
283
+ selectColumns(columns) {
284
+ // Early return if no columns specified
285
+ if (columns.length === 0)
286
+ return this;
287
+ this.query.query = `SELECT `;
288
+ // Build column list with proper comma separation
289
+ for (let i = 0; i < columns.length; i++) {
290
+ // Validate each column exists in table
291
+ if (!this.tableFields.includes(columns[i])) {
292
+ throw new Error("Table field: " +
293
+ columns[i] +
294
+ " does not exist in table: " +
295
+ this.tableName);
296
+ }
297
+ this.query.query += " " + columns[i];
298
+ // Add comma between columns, but not after the last one
299
+ if (i < columns.length - 1) {
300
+ this.query.query += " , ";
301
+ }
302
+ else {
303
+ this.query.query += " ";
304
+ }
305
+ }
306
+ this.query.query += ` FROM ${this.tableName} `;
307
+ return this;
308
+ }
309
+ selectDistinctColumns(columns) {
310
+ if (columns.length === 0)
311
+ return;
312
+ this.query.query = `SELECT DISTINCT `;
313
+ for (let i = 0; i < columns.length; i++) {
314
+ if (!this.tableFields.includes(columns[i])) {
315
+ throw new Error("Table field :" + columns[i] + " does not exist in table: " + this.tableName);
316
+ }
317
+ this.query.query += " " + columns[i];
318
+ if (i < columns.length - 1) {
319
+ this.query.query += " , ";
320
+ }
321
+ else {
322
+ this.query.query += " ";
323
+ }
324
+ }
325
+ this.query.query += ` FROM ${this.tableName}`;
326
+ return this;
327
+ }
328
+ orderBy(columnName) {
329
+ if (!this.tableFields.includes(columnName)) {
330
+ throw new Error("Field/Column : " + columnName + " does not exist in table : " + this.tableName);
331
+ }
332
+ this.query.query += ` ORDER BY ${columnName} `;
333
+ return this;
334
+ }
335
+ sort(sort = 1) {
336
+ this.query.query += " " + (sort > 0 ? "ASC" : "DESC") + " ";
337
+ return this;
338
+ }
339
+ limit(length) {
340
+ if (length <= 0)
341
+ return this;
342
+ this.query.query += ` LIMIT ${length} `;
343
+ return this;
344
+ }
345
+ offset(length) {
346
+ if (length <= 0)
347
+ return this;
348
+ this.query.query += ` OFFSET ${length} `;
349
+ return this;
350
+ }
351
+ insertOne(data) {
352
+ const column_name = Object.keys(data);
353
+ const column_value = Object.values(data);
354
+ if (column_name.length === 0 || column_value.length === 0)
355
+ return;
356
+ for (const field of column_name) {
357
+ if (!this.tableFields.includes(field)) {
358
+ throw new Error("Error: The column/fieldname : " + field + " does not exist in table: " + this.tableName);
359
+ }
360
+ }
361
+ const columns = column_name.join(", ");
362
+ const values = column_value;
363
+ this.query.query += ` INSERT INTO ${this.tableName} (${columns}) VALUES `;
364
+ this.query.query += ` ( `;
365
+ for (let i = 0; i < values.length; i++) {
366
+ this.query.query += ` ? `;
367
+ if (i < values.length - 1) {
368
+ this.query.query += ' , ';
369
+ }
370
+ if (i == values.length - 1) {
371
+ this.query.query += ` ) `;
372
+ }
373
+ }
374
+ this.query.supplies = values;
375
+ return this;
376
+ }
377
+ insertMany(fields, data = [[]]) {
378
+ if (fields.length === 0 || data.length === 0 || data.flat().length == 0)
379
+ return;
380
+ for (const field of fields) {
381
+ if (!this.tableFields.includes(field)) {
382
+ throw new Error("field/column_name " + field + " does not exists in table " + this.tableName);
383
+ }
384
+ }
385
+ const columns = fields.join(", ");
386
+ const placeholders = data.map(row => `(${row.map(() => '?').join(', ')})`).join(', ');
387
+ this.query.query = `INSERT INTO ${this.tableName} (${columns}) VALUES ${placeholders}`;
388
+ this.query.supplies = data.flat();
389
+ return this;
390
+ }
391
+ getFields() {
392
+ return [...this.tableFields];
393
+ }
394
+ getName() {
395
+ return this.tableName;
396
+ }
397
+ truncate() {
398
+ this.query.query = " TRUNCATE TABLE " + this.tableName;
399
+ return this;
400
+ }
401
+ /**
402
+ * Add WHERE clause to filter query results
403
+ * Use with comparison operators to filter rows
404
+ * Supports falsy values (0, false, empty string)
405
+ * @param field - Column name to filter on
406
+ * @param operator - SQL comparison operator (=, !=, <, >, LIKE, etc.)
407
+ * @param value - Value to compare against (any type)
408
+ * @returns this for method chaining
409
+ *
410
+ * @example
411
+ * table.selectAll().where('age', '>=', 18).execute();
412
+ * table.selectAll().where('active', '=', false).execute(); // Works with false!
413
+ * table.selectAll().where('count', '=', 0).execute(); // Works with 0!
414
+ */
415
+ where(field, operator, value) {
416
+ // Validate field exists in table
417
+ if (!this.tableFields.includes(field)) {
418
+ throw new Error(`Field/Column: ${field} does not exist in table: ${this.tableName}`);
419
+ }
420
+ // Validate operator is allowed
421
+ const validOperators = [
422
+ "=",
423
+ "!=",
424
+ "<>",
425
+ ">",
426
+ "<",
427
+ ">=",
428
+ "<=",
429
+ "LIKE",
430
+ "NOT LIKE",
431
+ "IN",
432
+ "NOT IN",
433
+ "IS",
434
+ "IS NOT",
435
+ ];
436
+ if (!validOperators.includes(operator.toUpperCase())) {
437
+ throw new Error(`Invalid operator: ${operator}`);
438
+ }
439
+ // Build WHERE clause with placeholder
440
+ this.query.query += ` WHERE ${field} ${operator} ? `;
441
+ this.query.supplies.push(value);
442
+ return this;
443
+ }
444
+ /**
445
+ * Add AND condition to existing WHERE clause
446
+ * Chain multiple conditions together
447
+ * @param field - Column name
448
+ * @param operator - SQL comparison operator
449
+ * @param value - Value to compare (any type including 0, false)
450
+ * @returns this for method chaining
451
+ *
452
+ * @example
453
+ * table.selectAll()
454
+ * .where('age', '>=', 18)
455
+ * .and('active', '=', true)
456
+ * .execute();
457
+ */
458
+ and(field, operator, value) {
459
+ if (!this.tableFields.includes(field)) {
460
+ throw new Error(`Field/Column: ${field} does not exist in table: ${this.tableName}`);
461
+ }
462
+ const validOperators = [
463
+ "=",
464
+ "!=",
465
+ "<>",
466
+ ">",
467
+ "<",
468
+ ">=",
469
+ "<=",
470
+ "LIKE",
471
+ "NOT LIKE",
472
+ "IN",
473
+ "NOT IN",
474
+ "IS",
475
+ "IS NOT",
476
+ ];
477
+ if (!validOperators.includes(operator.toUpperCase())) {
478
+ throw new Error(`Invalid operator: ${operator}`);
479
+ }
480
+ this.query.query += ` AND ${field} ${operator} ? `;
481
+ this.query.supplies.push(value);
482
+ return this;
483
+ }
484
+ /**
485
+ * Add OR condition to existing WHERE clause
486
+ * Combine alternative conditions
487
+ * @param field - Column name
488
+ * @param operator - SQL comparison operator
489
+ * @param value - Value to compare (any type including 0, false)
490
+ * @returns this for method chaining
491
+ *
492
+ * @example
493
+ * table.selectAll()
494
+ * .where('role', '=', 'admin')
495
+ * .or('role', '=', 'moderator')
496
+ * .execute();
497
+ */
498
+ or(field, operator, value) {
499
+ if (!this.tableFields.includes(field)) {
500
+ throw new Error(`Field/Column: ${field} does not exist in table: ${this.tableName}`);
501
+ }
502
+ const validOperators = [
503
+ "=",
504
+ "!=",
505
+ "<>",
506
+ ">",
507
+ "<",
508
+ ">=",
509
+ "<=",
510
+ "LIKE",
511
+ "NOT LIKE",
512
+ "IN",
513
+ "NOT IN",
514
+ "IS",
515
+ "IS NOT",
516
+ ];
517
+ if (!validOperators.includes(operator.toUpperCase())) {
518
+ throw new Error(`Invalid operator: ${operator}`);
519
+ }
520
+ this.query.query += ` OR ${field} ${operator} ? `;
521
+ this.query.supplies.push(value);
522
+ return this;
523
+ }
524
+ update(data) {
525
+ const entries = Object.entries(data);
526
+ if (entries.length === 0)
527
+ return;
528
+ this.query.query = `UPDATE ${this.tableName} SET `;
529
+ entries.forEach(([key, value], index) => {
530
+ if (!this.tableFields.includes(key)) {
531
+ throw new Error(`Field/Column: ${key} does not exist in table: ${this.tableName}`);
532
+ }
533
+ this.query.query += `${key} = ?`;
534
+ if (index < entries.length - 1) {
535
+ this.query.query += ", ";
536
+ }
537
+ this.query.supplies.push(value);
538
+ });
539
+ return this;
540
+ }
541
+ join(type, table, condition) {
542
+ const validTypes = ['INNER', 'LEFT', 'RIGHT', 'FULL'];
543
+ if (!validTypes.includes(type)) {
544
+ throw new Error(`Invalid join type: ${type}`);
545
+ }
546
+ this.query.query += ` ${type} JOIN ${table} ON ${condition.left} ${condition.operator} ${condition.right} `;
547
+ return this;
548
+ }
549
+ delete() {
550
+ this.query.query = `DELETE FROM ${this.tableName}`;
551
+ return this;
552
+ }
553
+ whereIn(field, values) {
554
+ if (!this.tableFields.includes(field)) {
555
+ throw new Error(`Field/Column: ${field} does not exist in table: ${this.tableName}`);
556
+ }
557
+ if (!Array.isArray(values) || values.length === 0) {
558
+ throw new Error("whereIn requires a non-empty array of values");
559
+ }
560
+ const placeholders = values.map(() => '?').join(', ');
561
+ this.query.query += ` WHERE ${field} IN (${placeholders}) `;
562
+ this.query.supplies.push(...values);
563
+ return this;
564
+ }
565
+ whereNotIn(field, values) {
566
+ if (!field || values.length === 0)
567
+ return;
568
+ if (!this.tableFields.includes(field)) {
569
+ throw new Error(`Field/Column: ${field} does not exist in table: ${this.tableName}`);
570
+ }
571
+ const placeholders = values.map(() => '?').join(', ');
572
+ this.query.query += ` WHERE ${field} NOT IN (${placeholders}) `;
573
+ this.query.supplies.push(...values);
574
+ return this;
575
+ }
576
+ getQuery() {
577
+ return this.query;
578
+ }
579
+ execute() {
580
+ return this.query.execute();
581
+ }
582
+ customQuery(query, supplies) {
583
+ return this.query.customQuery(query, supplies);
584
+ }
585
+ }
586
+ /**
587
+ * Mango - Main ORM class for MySQL database operations
588
+ * Manages connection pool and provides table instances
589
+ * Auto-discovers existing tables on connection
590
+ *
591
+ * @example
592
+ * const mango = new Mango();
593
+ * await mango.connect({
594
+ * host: 'localhost',
595
+ * user: 'root',
596
+ * password: 'pass',
597
+ * database: 'mydb'
598
+ * });
599
+ *
600
+ * const users = mango.selectTable('users');
601
+ * const results = await users.selectAll().execute();
602
+ */
603
+ class Mango {
604
+ constructor() {
605
+ this.tables = []; // Cached table instances
606
+ this.query = new MangoQuery();
607
+ }
608
+ async connect({ host, user, password, database, connectionLimit = 10, waitForConnection = true, queueLimit = 0, connectTimeout = 10000, charset = "utf8mb4", }) {
609
+ this.db = sql.createPool({
610
+ host: host,
611
+ user: user,
612
+ password: password,
613
+ database: database,
614
+ connectionLimit: connectionLimit,
615
+ waitForConnections: waitForConnection,
616
+ queueLimit: queueLimit,
617
+ connectTimeout: connectTimeout,
618
+ charset: charset,
619
+ });
620
+ this.query.config(this.db);
621
+ const tables = await this.query.customQuery("SHOW TABLES", []);
622
+ const tableNames = tables.map((row) => Object.values(row)[0]);
623
+ for (const name of tableNames) {
624
+ const columns = await this.query.customQuery("SELECT column_name FROM information_schema.columns WHERE table_schema=? AND table_name=?", [database, name]);
625
+ const column_names = columns.map((row) => row.column_name);
626
+ this.tables.push(new MangoTable(this.db, name, column_names));
627
+ }
628
+ return this;
629
+ }
630
+ async disconnect() {
631
+ return new Promise((resolve, reject) => {
632
+ this.db.end((err) => {
633
+ if (err)
634
+ reject(err);
635
+ else {
636
+ this.tables = [];
637
+ resolve();
638
+ }
639
+ });
640
+ });
641
+ }
642
+ /**
643
+ * Get a new MangoType instance for schema building
644
+ * Use when creating or modifying table columns
645
+ * @returns New MangoType instance for chaining
646
+ *
647
+ * @example
648
+ * const idField = mango.types().int().autoIncrement().primaryKey();
649
+ */
650
+ types() {
651
+ return new MangoType();
652
+ }
653
+ /**
654
+ * Get a table instance by name
655
+ * Returns strongly-typed table if generic provided
656
+ * @param name - Name of the table
657
+ * @returns MangoTable instance for building queries
658
+ *
659
+ * @example
660
+ * interface User { id: number; name: string; email: string }
661
+ * const users = mango.selectTable<User>('users');
662
+ */
663
+ selectTable(name) {
664
+ for (const table of this.tables) {
665
+ if (table.getName() == name.toLowerCase()) {
666
+ return table;
667
+ }
668
+ }
669
+ throw new Error("Table not found: " + name);
670
+ }
671
+ /**
672
+ * Get all discovered table instances
673
+ * @returns Array of all MangoTable instances
674
+ */
675
+ getTables() {
676
+ return [...this.tables];
677
+ }
678
+ async createTable(name, fields) {
679
+ this.query.query = "CREATE TABLE " + name + "( \n";
680
+ const fieldEnteries = Object.entries(fields);
681
+ let table = new MangoTable(this.db, name.toLowerCase(), [
682
+ ...fieldEnteries.map(([key, value], index) => {
683
+ return key;
684
+ }),
685
+ ]);
686
+ fieldEnteries.forEach(([key, value], index) => {
687
+ this.query.query += key + " " + value.getQuery();
688
+ if (index < fieldEnteries.length - 1) {
689
+ this.query.query += ", \n";
690
+ }
691
+ });
692
+ this.query.query += "\n)";
693
+ await this.query.execute();
694
+ this.query.query = "";
695
+ this.tables.push(table);
696
+ return table;
697
+ }
698
+ async dropTable(name) {
699
+ for (let i = 0; i < this.tables.length; i++) {
700
+ if (this.tables[i].getName() === name) {
701
+ this.query.query = "DROP TABLE " + name;
702
+ await this.query.execute();
703
+ this.query.query = "";
704
+ this.tables.splice(i, 1);
705
+ return;
706
+ }
707
+ }
708
+ }
709
+ customQuery(query, supplies) {
710
+ return this.query.customQuery(query, supplies);
711
+ }
712
+ }
713
+ export { Mango, MangoType, MangoTable };