@iamkirbki/database-handler-core 3.1.4 → 4.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/dist/abstract/Model.d.ts +42 -16
- package/dist/abstract/Model.d.ts.map +1 -1
- package/dist/abstract/Model.js +159 -32
- package/dist/abstract/{Schema.d.ts → SchemaTableBuilder.d.ts} +3 -8
- package/dist/abstract/SchemaTableBuilder.d.ts.map +1 -0
- package/dist/abstract/{Schema.js → SchemaTableBuilder.js} +1 -3
- package/dist/base/Database.d.ts +13 -0
- package/dist/base/Database.d.ts.map +1 -0
- package/dist/base/Database.js +27 -0
- package/dist/base/Query.d.ts +43 -0
- package/dist/base/Query.d.ts.map +1 -0
- package/dist/base/Query.js +87 -0
- package/dist/base/Record.d.ts +23 -0
- package/dist/base/Record.d.ts.map +1 -0
- package/dist/base/Record.js +72 -0
- package/dist/base/Table.d.ts +27 -0
- package/dist/base/Table.d.ts.map +1 -0
- package/dist/base/Table.js +128 -0
- package/dist/helpers/QueryStatementBuilder.d.ts +4 -35
- package/dist/helpers/QueryStatementBuilder.d.ts.map +1 -1
- package/dist/helpers/QueryStatementBuilder.js +6 -37
- package/dist/index.d.ts +13 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -8
- package/dist/interfaces/IController.d.ts +8 -0
- package/dist/interfaces/IController.d.ts.map +1 -0
- package/dist/interfaces/IController.js +1 -0
- package/dist/interfaces/IDatabaseAdapter.d.ts.map +1 -1
- package/dist/interfaces/IMigration.d.ts +6 -0
- package/dist/interfaces/IMigration.d.ts.map +1 -0
- package/dist/interfaces/IMigration.js +1 -0
- package/dist/interfaces/ISchemaBuilder.d.ts +7 -0
- package/dist/interfaces/ISchemaBuilder.d.ts.map +1 -0
- package/dist/interfaces/ISchemaBuilder.js +1 -0
- package/dist/runtime/Container.d.ts +12 -0
- package/dist/runtime/Container.d.ts.map +1 -0
- package/dist/runtime/Container.js +29 -0
- package/dist/runtime/Repository.d.ts +18 -0
- package/dist/runtime/Repository.d.ts.map +1 -0
- package/dist/runtime/Repository.js +110 -0
- package/dist/types/index.d.ts +1 -4
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/model.d.ts +71 -0
- package/dist/types/model.d.ts.map +1 -0
- package/dist/types/model.js +2 -0
- package/dist/types/table.d.ts +6 -2
- package/dist/types/table.d.ts.map +1 -1
- package/package.json +5 -4
- package/dist/Database.d.ts +0 -98
- package/dist/Database.d.ts.map +0 -1
- package/dist/Database.js +0 -126
- package/dist/Query.d.ts +0 -143
- package/dist/Query.d.ts.map +0 -1
- package/dist/Query.js +0 -205
- package/dist/Record.d.ts +0 -125
- package/dist/Record.d.ts.map +0 -1
- package/dist/Record.js +0 -174
- package/dist/Table.d.ts +0 -158
- package/dist/Table.d.ts.map +0 -1
- package/dist/Table.js +0 -258
- package/dist/abstract/Controller.d.ts +0 -13
- package/dist/abstract/Controller.d.ts.map +0 -1
- package/dist/abstract/Controller.js +0 -6
- package/dist/abstract/Migration.d.ts +0 -6
- package/dist/abstract/Migration.d.ts.map +0 -1
- package/dist/abstract/Migration.js +0 -2
- package/dist/abstract/Schema.d.ts.map +0 -1
- package/dist/abstract/User.d.ts +0 -8
- package/dist/abstract/User.d.ts.map +0 -1
- package/dist/abstract/User.js +0 -6
package/dist/Table.js
DELETED
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
import Query from "./Query.js";
|
|
11
|
-
import QueryStatementBuilder from "./helpers/QueryStatementBuilder.js";
|
|
12
|
-
/**
|
|
13
|
-
* Table class for interacting with a specific database table
|
|
14
|
-
* Provides methods for querying, inserting, and retrieving table metadata
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* ```typescript
|
|
18
|
-
* const users = db.Table('users');
|
|
19
|
-
*
|
|
20
|
-
* // Get all records
|
|
21
|
-
* const allUsers = users.Records();
|
|
22
|
-
*
|
|
23
|
-
* // Get filtered records
|
|
24
|
-
* const activeUsers = users.Records({
|
|
25
|
-
* where: { status: 'active' }
|
|
26
|
-
* });
|
|
27
|
-
*
|
|
28
|
-
* // Insert a record
|
|
29
|
-
* users.Insert({ name: 'John', email: 'john@example.com' });
|
|
30
|
-
* ```
|
|
31
|
-
*/
|
|
32
|
-
export default class Table {
|
|
33
|
-
/**
|
|
34
|
-
* Private constructor - use Table.create() instead
|
|
35
|
-
*
|
|
36
|
-
* @param name - Name of the table
|
|
37
|
-
* @param adapter - Database adapter instance
|
|
38
|
-
*/
|
|
39
|
-
constructor(name, adapter) {
|
|
40
|
-
this.name = name;
|
|
41
|
-
this.adapter = adapter;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Create a Table instance (async factory method)
|
|
45
|
-
*
|
|
46
|
-
* @param name - Name of the table
|
|
47
|
-
* @param adapter - Database adapter instance
|
|
48
|
-
* @param skipValidation - Skip table existence validation (used when creating new tables)
|
|
49
|
-
* @returns Table instance
|
|
50
|
-
* @throws Error if the table does not exist in the database
|
|
51
|
-
*/
|
|
52
|
-
static create(name_1, adapter_1) {
|
|
53
|
-
return __awaiter(this, arguments, void 0, function* (name, adapter, skipValidation = false) {
|
|
54
|
-
const table = new Table(name, adapter);
|
|
55
|
-
if (!skipValidation) {
|
|
56
|
-
const columns = yield table.TableColumnInformation();
|
|
57
|
-
if (!columns.length) {
|
|
58
|
-
throw new Error(`Table "${name}" does not exist in the database.\nYou might want to use the CreateTable function.`);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return table;
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Get the name of the table
|
|
66
|
-
*
|
|
67
|
-
* @returns The table name
|
|
68
|
-
*/
|
|
69
|
-
get Name() {
|
|
70
|
-
return this.name;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Get raw column information from SQLite PRAGMA
|
|
74
|
-
*
|
|
75
|
-
* @returns Array of column metadata from SQLite
|
|
76
|
-
*
|
|
77
|
-
* @example
|
|
78
|
-
* ```typescript
|
|
79
|
-
* const columns = await table.TableColumnInformation();
|
|
80
|
-
* // [{ cid: 0, name: 'id', type: 'INTEGER', notnull: 0, dflt_value: null, pk: 1 }, ...]
|
|
81
|
-
* ```
|
|
82
|
-
*/
|
|
83
|
-
TableColumnInformation() {
|
|
84
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
85
|
-
return this.adapter.tableColumnInformation(this.name);
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Get readable, formatted column information
|
|
90
|
-
*
|
|
91
|
-
* @returns Array of formatted column metadata with readable properties
|
|
92
|
-
*
|
|
93
|
-
* @example
|
|
94
|
-
* ```typescript
|
|
95
|
-
* const columns = await table.ReadableTableColumnInformation();
|
|
96
|
-
* // [{ name: 'id', type: 'INTEGER', nullable: false, isPrimaryKey: true, defaultValue: null }, ...]
|
|
97
|
-
* ```
|
|
98
|
-
*/
|
|
99
|
-
ReadableTableColumnInformation() {
|
|
100
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
101
|
-
const columns = yield this.TableColumnInformation();
|
|
102
|
-
return columns.map((col) => ({
|
|
103
|
-
name: col.name,
|
|
104
|
-
type: col.type,
|
|
105
|
-
nullable: col.notnull === 0,
|
|
106
|
-
isPrimaryKey: col.pk === 1,
|
|
107
|
-
defaultValue: col.dflt_value,
|
|
108
|
-
}));
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
Drop() {
|
|
112
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
113
|
-
const queryStr = `DROP TABLE IF EXISTS "${this.name}";`;
|
|
114
|
-
const query = new Query(this, queryStr, this.adapter);
|
|
115
|
-
yield query.Run();
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Fetch records from the table with optional filtering, ordering, and pagination
|
|
120
|
-
* @param options Query options for selecting records
|
|
121
|
-
* @returns Array of records matching the criteria
|
|
122
|
-
*
|
|
123
|
-
* @example
|
|
124
|
-
* // Get all records
|
|
125
|
-
* table.Records();
|
|
126
|
-
*
|
|
127
|
-
* @example
|
|
128
|
-
* // Get specific columns with filters
|
|
129
|
-
* table.Records({
|
|
130
|
-
* select: 'id, name',
|
|
131
|
-
* where: { status: 'active', age: 25 }
|
|
132
|
-
* });
|
|
133
|
-
*
|
|
134
|
-
* @example
|
|
135
|
-
* // With ordering and pagination
|
|
136
|
-
* table.Records({
|
|
137
|
-
* orderBy: 'created_at DESC',
|
|
138
|
-
* limit: 10,
|
|
139
|
-
* offset: 20
|
|
140
|
-
* });
|
|
141
|
-
*/
|
|
142
|
-
Records(options) {
|
|
143
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
144
|
-
const queryStr = QueryStatementBuilder.BuildSelect(this, {
|
|
145
|
-
select: options === null || options === void 0 ? void 0 : options.select,
|
|
146
|
-
where: options === null || options === void 0 ? void 0 : options.where,
|
|
147
|
-
orderBy: options === null || options === void 0 ? void 0 : options.orderBy,
|
|
148
|
-
limit: options === null || options === void 0 ? void 0 : options.limit,
|
|
149
|
-
offset: options === null || options === void 0 ? void 0 : options.offset,
|
|
150
|
-
});
|
|
151
|
-
const query = new Query(this, queryStr, this.adapter);
|
|
152
|
-
if ((options === null || options === void 0 ? void 0 : options.where) && Object.keys(options.where).length > 0)
|
|
153
|
-
query.Parameters = options.where;
|
|
154
|
-
const results = yield query.All();
|
|
155
|
-
// Wrap each result in a Record object
|
|
156
|
-
return results;
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Fetch a single record from the table
|
|
161
|
-
*
|
|
162
|
-
* @param options - Query options for selecting a record
|
|
163
|
-
* @param options.select - Columns to select (default: "*")
|
|
164
|
-
* @param options.where - Filter conditions as key-value pairs
|
|
165
|
-
* @param options.orderBy - SQL ORDER BY clause (e.g., "created_at DESC")
|
|
166
|
-
* @returns Single Record instance or undefined if not found
|
|
167
|
-
*
|
|
168
|
-
* @example
|
|
169
|
-
* ```typescript
|
|
170
|
-
* // Get record by ID
|
|
171
|
-
* const user = table.Record({ where: { id: 1 } });
|
|
172
|
-
*
|
|
173
|
-
* // Get most recent record
|
|
174
|
-
* const latest = table.Record({ orderBy: 'created_at DESC' });
|
|
175
|
-
*
|
|
176
|
-
* // Update the record
|
|
177
|
-
* user?.update({ status: 'inactive' });
|
|
178
|
-
* ```
|
|
179
|
-
*/
|
|
180
|
-
Record(options) {
|
|
181
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
182
|
-
const results = yield this.Records({
|
|
183
|
-
select: options === null || options === void 0 ? void 0 : options.select,
|
|
184
|
-
where: options === null || options === void 0 ? void 0 : options.where,
|
|
185
|
-
orderBy: options === null || options === void 0 ? void 0 : options.orderBy,
|
|
186
|
-
limit: 1
|
|
187
|
-
});
|
|
188
|
-
return results[0];
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* Get the total count of records in the table
|
|
193
|
-
*
|
|
194
|
-
* @returns Number of records in the table
|
|
195
|
-
*
|
|
196
|
-
* @example
|
|
197
|
-
* ```typescript
|
|
198
|
-
* const totalUsers = table.RecordsCount;
|
|
199
|
-
* console.log(`Total users: ${totalUsers}`);
|
|
200
|
-
* ```
|
|
201
|
-
*/
|
|
202
|
-
RecordsCount() {
|
|
203
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
204
|
-
const stmt = yield this.adapter.prepare(`SELECT COUNT(*) as count FROM "${this.name}";`);
|
|
205
|
-
const count = yield stmt.get({});
|
|
206
|
-
return parseInt(count.count) || 0;
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
/**
|
|
210
|
-
* Insert one or multiple records into the table
|
|
211
|
-
* Validates data types against table schema before insertion
|
|
212
|
-
*
|
|
213
|
-
* @param values - Single object or array of objects to insert
|
|
214
|
-
* @returns Insert result with lastInsertRowid and changes count
|
|
215
|
-
* @throws Error if values is empty or contains no columns
|
|
216
|
-
* @throws Error if validation fails (wrong types, missing required fields)
|
|
217
|
-
*
|
|
218
|
-
* @example
|
|
219
|
-
* ```typescript
|
|
220
|
-
* // Insert single record
|
|
221
|
-
* const result = table.Insert({ name: 'John', age: 30 });
|
|
222
|
-
* console.log(`Inserted row ID: ${result.lastInsertRowid}`);
|
|
223
|
-
*
|
|
224
|
-
* // Insert multiple records (uses transaction for atomicity)
|
|
225
|
-
* table.Insert([
|
|
226
|
-
* { name: 'John', age: 30 },
|
|
227
|
-
* { name: 'Jane', age: 25 }
|
|
228
|
-
* ]);
|
|
229
|
-
* ```
|
|
230
|
-
*/
|
|
231
|
-
Insert(values) {
|
|
232
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
233
|
-
var _a, _b;
|
|
234
|
-
const columns = Object.keys(values);
|
|
235
|
-
if (columns.length === 0) {
|
|
236
|
-
throw new Error("Cannot insert record with no columns");
|
|
237
|
-
}
|
|
238
|
-
const queryStr = QueryStatementBuilder.BuildInsert(this, values);
|
|
239
|
-
const query = new Query(this, queryStr, this.adapter);
|
|
240
|
-
query.Parameters = values;
|
|
241
|
-
const result = yield query.Run();
|
|
242
|
-
let recordId;
|
|
243
|
-
// For PostgreSQL compatibility: use 'id' from values if lastInsertRowid is undefined
|
|
244
|
-
if (Array.isArray(values)) {
|
|
245
|
-
recordId = (_a = result === null || result === void 0 ? void 0 : result.lastInsertRowid) !== null && _a !== void 0 ? _a : values.map(v => v.column === 'id' ? v.value : undefined);
|
|
246
|
-
}
|
|
247
|
-
else {
|
|
248
|
-
recordId = (_b = result === null || result === void 0 ? void 0 : result.lastInsertRowid) !== null && _b !== void 0 ? _b : values.id;
|
|
249
|
-
}
|
|
250
|
-
if (recordId === undefined) {
|
|
251
|
-
return undefined;
|
|
252
|
-
}
|
|
253
|
-
return this.Record({
|
|
254
|
-
where: { id: recordId }
|
|
255
|
-
});
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import IDatabaseAdapter from "@core/interfaces/IDatabaseAdapter";
|
|
2
|
-
import Model from "./Model";
|
|
3
|
-
export default abstract class Controller<M extends Model<object>> {
|
|
4
|
-
protected adapter: IDatabaseAdapter;
|
|
5
|
-
protected Model: new (adapter: IDatabaseAdapter, data?: object) => M;
|
|
6
|
-
constructor(adapter: IDatabaseAdapter, Model: new (adapter: IDatabaseAdapter, data?: object) => M);
|
|
7
|
-
abstract index(): Promise<M[]>;
|
|
8
|
-
abstract show(id: string): Promise<M | undefined>;
|
|
9
|
-
abstract create(data: object): Promise<M>;
|
|
10
|
-
abstract update(id: string, data: object): Promise<void>;
|
|
11
|
-
abstract delete(id: string): Promise<void>;
|
|
12
|
-
}
|
|
13
|
-
//# sourceMappingURL=Controller.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Controller.d.ts","sourceRoot":"","sources":["../../src/abstract/Controller.ts"],"names":[],"mappings":"AAAA,OAAO,gBAAgB,MAAM,mCAAmC,CAAC;AACjE,OAAO,KAAK,MAAM,SAAS,CAAC;AAE5B,MAAM,CAAC,OAAO,CAAC,QAAQ,OAAO,UAAU,CAAC,CAAC,SAAS,KAAK,CAAC,MAAM,CAAC;IAExD,SAAS,CAAC,OAAO,EAAE,gBAAgB;IACnC,SAAS,CAAC,KAAK,EAAE,KAAK,OAAO,EAAE,gBAAgB,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,CAAC;gBAD1D,OAAO,EAAE,gBAAgB,EACzB,KAAK,EAAE,KAAK,OAAO,EAAE,gBAAgB,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,CAAC;IAGxE,QAAQ,CAAC,KAAK,IAAI,OAAO,CAAC,CAAC,EAAE,CAAC;IAC9B,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IACjD,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IACxD,QAAQ,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAC7C"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Migration.d.ts","sourceRoot":"","sources":["../../src/abstract/Migration.ts"],"names":[],"mappings":"AAAA,OAAO,gBAAgB,MAAM,mCAAmC,CAAC;AAEjE,8BAAsB,SAAS;IAC3B,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAChD,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;CACrD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Schema.d.ts","sourceRoot":"","sources":["../../src/abstract/Schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD,8BAAsB,qBAAqB;IACvC,QAAQ,CAAC,WAAW,CAChB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,GAC9C,OAAO,CAAC,IAAI,CAAC;IAEhB,QAAQ,CAAC,SAAS,CACd,IAAI,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC;IAEhB,QAAQ,CAAC,UAAU,CACf,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,GAC9C,OAAO,CAAC,IAAI,CAAC;CACnB;AAED,8BAAsB,kBAAkB;IACpC,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,CAAM;IAE3C,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI;IAoBjD,QAAQ,CAAC,KAAK,IAAI,MAAM;IAExB,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IACvC,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IACvC,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,IAAI;IAExF,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IACjC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IACpD,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IACpC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IACpC,QAAQ,CAAC,UAAU,IAAI,IAAI;CAC9B"}
|
package/dist/abstract/User.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"User.d.ts","sourceRoot":"","sources":["../../src/abstract/User.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,YAAY,CAAC;AAE/B,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;CAAE,CAAC;IAC3D,UAAU,IAAI,IAAI;CAG5B"}
|