squilo 0.1.2 → 0.2.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.
Files changed (40) hide show
  1. package/README.md +265 -36
  2. package/package.json +17 -12
  3. package/src/.DS_Store +0 -0
  4. package/src/index.ts +3 -1
  5. package/src/pipes/auth/index.ts +2 -12
  6. package/src/pipes/auth/strategies/msal.ts +14 -7
  7. package/src/pipes/auth/strategies/userAndPassword.ts +1 -1
  8. package/src/pipes/auth/types.ts +12 -0
  9. package/src/pipes/connect/index.ts +25 -42
  10. package/src/pipes/connect/types.ts +25 -0
  11. package/src/pipes/execute/index.ts +36 -4
  12. package/src/pipes/index.ts +1 -1
  13. package/src/pipes/input/index.ts +6 -11
  14. package/src/pipes/input/types.ts +7 -0
  15. package/src/pipes/output/index.ts +4 -3
  16. package/src/pipes/output/strategies/console.ts +1 -1
  17. package/src/pipes/output/strategies/json.spec.ts +16 -6
  18. package/src/pipes/output/strategies/json.ts +14 -3
  19. package/src/pipes/output/strategies/merge.ts +1 -1
  20. package/src/pipes/output/strategies/xls.spec.ts +44 -15
  21. package/src/pipes/output/strategies/xls.ts +42 -10
  22. package/src/pipes/output/types.ts +1 -0
  23. package/src/pipes/retrieve/index.ts +48 -19
  24. package/src/pipes/retrieve/types.ts +5 -0
  25. package/src/pipes/server/index.ts +2 -6
  26. package/src/pipes/server/types.ts +5 -0
  27. package/src/pipes/types.ts +1 -0
  28. package/src/utils/append-error.ts +42 -0
  29. package/src/utils/load-env.ts +18 -0
  30. package/biome.json +0 -34
  31. package/test/connect.spec.ts +0 -119
  32. package/test/container/container.spec.ts +0 -33
  33. package/test/container/container.ts +0 -22
  34. package/test/container/setup/databases.spec.ts +0 -24
  35. package/test/container/setup/databases.ts +0 -77
  36. package/test/container/setup/users.spec.ts +0 -25
  37. package/test/container/setup/users.ts +0 -54
  38. package/test/index.spec.ts +0 -68
  39. package/test/input.spec.ts +0 -64
  40. package/tsconfig.json +0 -28
package/README.md CHANGED
@@ -20,64 +20,293 @@ bun init
20
20
  bun add squilo
21
21
  ```
22
22
 
23
- ## Usage Example
23
+ ## Usage Examples
24
24
 
25
- ### Exportable Servers (Production, Dev, ...)
25
+ ### Server Configuration with Azure AD Authentication
26
26
 
27
27
  ```ts
28
- // src/server.ts
28
+ // servers/production.ts
29
29
  import { Server } from "squilo";
30
+ import { ActiveDirectoryAccessToken } from "squilo/auth/strategies";
30
31
 
31
- export const ProdServer = Server({
32
- server: "prod-host",
33
- port: 1433,
34
- options: { encrypt: true }
35
- });
32
+ export const Production = Server({
33
+ server: "your-server.database.windows.net",
34
+ options: {
35
+ encrypt: true,
36
+ trustedConnection: true,
37
+ trustServerCertificate: false
38
+ },
39
+ requestTimeout: 300000
40
+ }).Auth(await ActiveDirectoryAccessToken({
41
+ clientId: "your-client-id",
42
+ clientSecret: "your-client-secret",
43
+ authority: "https://login.microsoftonline.com/your-tenant-id"
44
+ }));
45
+ ```
36
46
 
37
- export const DevServer = Server({
38
- server: "localhost",
39
- port: 1433,
40
- options: { encrypt: false }
41
- });
47
+ ### Dynamic Database Connections
48
+
49
+ ```ts
50
+ // databases/all-active.ts
51
+ import { Production } from "../servers/production";
52
+ import { Qas } from "../servers/qas";
53
+
54
+ // Connect to multiple databases dynamically from Production
55
+ export const AllActiveDatabases = Production
56
+ .Connect({
57
+ database: "ManagerDatabase",
58
+ query: "SELECT [Database] FROM Client WHERE Active = 1"
59
+ });
60
+
61
+ // Same pattern for QAS environment
62
+ export const AllActiveQasDatabases = Qas
63
+ .Connect({
64
+ database: "ManagerDatabase",
65
+ query: "SELECT [Database] FROM Client WHERE Active = 1"
66
+ });
67
+
68
+ // Simple database connection
69
+ export const TestDatabase = Production.Connect("TestDatabase");
42
70
  ```
43
71
 
44
- ### Exportable Authentications
72
+ ### Data Retrieval with Output Strategies
45
73
 
46
74
  ```ts
47
- // src/auth.ts
48
- import { UserAndPassword } from "squilo/pipes/auth/strategies";
75
+ // scripts/user-report.ts
76
+ import { JsonOutputStrategy, XlsOutputStrategy } from "squilo/output/strategies";
77
+ import { Production } from "../servers/production";
78
+ import { AllActiveDatabases } from "../databases/all-active";
79
+
80
+ type UserData = {
81
+ Id: number;
82
+ Email: string;
83
+ CreatedAt: Date;
84
+ }
49
85
 
50
- export const ProdAuth = UserAndPassword("prod_user", "prod_pass");
51
- export const DevAuth = UserAndPassword("sa", "your_dev_password");
86
+ // Retrieve data from multiple databases (uses Production by default)
87
+ await AllActiveDatabases
88
+ .Retrieve(async (transaction, database) => {
89
+ const result = await transaction
90
+ .request()
91
+ .query<UserData>`
92
+ SELECT Id, Email, CreatedAt
93
+ FROM Users
94
+ WHERE Active = 1
95
+ ORDER BY CreatedDate DESC
96
+ `;
97
+ return result.recordset;
98
+ })
99
+ .Output(JsonOutputStrategy());
52
100
  ```
53
101
 
54
- ### Create Scripts
102
+ ### Execute Operations Across Multiple Databases
55
103
 
56
104
  ```ts
57
- // scripts/fixEmails.ts
58
- import { ProdServer } from "../src/server";
59
- import { ProdAuth } from "../src/auth";
60
-
61
- await ProdServer.Auth(ProdAuth)
62
- .Connect(["db1", "db2"])
63
- .Execute(async (transaction) => {
64
- await transaction.request().query`
65
- UPDATE Users SET Email = RTRIM(Email)
66
- `;
67
- });
105
+ // scripts/update-campaign-status.ts
106
+ import { AllActiveDatabases } from "../databases/all-active";
107
+ import { Production } from "../servers/production";
108
+
109
+ await AllActiveDatabases
110
+ .Execute(async (transaction, database) => {
111
+ await transaction.request().query`
112
+ UPDATE Campaigns
113
+ SET Status = 'Active'
114
+ WHERE EndDate IS NULL AND StartDate <= GETDATE()
115
+ `;
116
+ })
117
+ .then(() => process.exit(0));
68
118
  ```
69
119
 
70
- ### Run Scripts Using Bun
120
+ ### Complex Queries with TypeScript Types
71
121
 
72
- ```bash
73
- bun run scripts/fixEmails.ts
122
+ ```ts
123
+ // scripts/analytics-report.ts
124
+ import { JsonOutputStrategy } from "squilo/output/strategies";
125
+ import { Production } from "../servers/production";
126
+
127
+ type AnalyticsResult = {
128
+ MaxConnectedStates: number;
129
+ }
130
+
131
+ await Production
132
+ .Connect("AnalyticsDatabase")
133
+ .Retrieve(async (transaction) => {
134
+ const result = await transaction.request().query<AnalyticsResult>`
135
+ -- Complex analytical query with CTEs and cursors
136
+ WITH ActiveStates AS (
137
+ SELECT DISTINCT State.Id
138
+ FROM State
139
+ INNER JOIN FlowState ON FlowState.StateId = State.Id
140
+ WHERE Flow.EndDate IS NULL
141
+ )
142
+ SELECT MAX(ConnectedCount) As MaxConnectedStates
143
+ FROM (
144
+ SELECT COUNT(*) as ConnectedCount
145
+ FROM ActiveStates
146
+ GROUP BY StateGroup
147
+ ) AS StateCounts;
148
+ `;
149
+ return result.recordset;
150
+ })
151
+ .Output(JsonOutputStrategy());
74
152
  ```
75
153
 
76
154
  ## Advanced Usage
77
155
 
78
- - Use `.Input()` to provide dynamic input for scripts
79
- - Use `.Retrieve()` to fetch results from databases
80
- - Use `.Output()` strategies to merge or format results
156
+ ### Authentication Strategies
157
+
158
+ Squilo supports multiple authentication methods:
159
+
160
+ ```ts
161
+ // User and Password authentication
162
+ import { UserAndPassword } from "squilo/auth/strategies";
163
+ const auth = UserAndPassword("username", "password");
164
+
165
+ // Active Directory Access Token for Azure SQL
166
+ import { ActiveDirectoryAccessToken } from "squilo/auth/strategies";
167
+ const azureAuth = await ActiveDirectoryAccessToken({
168
+ clientId: "your-client-id",
169
+ clientSecret: "your-client-secret",
170
+ authority: "https://login.microsoftonline.com/your-tenant-id"
171
+ });
172
+ ```
173
+
174
+ ### Input and Retrieval
175
+
176
+ ```ts
177
+ // Retrieve data with typed results
178
+ type ConfigData = {
179
+ Id: number;
180
+ Setting: string;
181
+ Value: string;
182
+ }
183
+
184
+ const results = await Production
185
+ .Connect("ConfigDatabase")
186
+ .Retrieve(async (transaction) => {
187
+ const result = await transaction
188
+ .request()
189
+ .query<ConfigData>`
190
+ SELECT Id, Setting, Value
191
+ FROM Configuration
192
+ ORDER BY CreatedDate
193
+ `;
194
+ return result.recordset;
195
+ });
196
+
197
+ // Execute with database context
198
+ await AllActiveDatabases
199
+ .Execute(async (transaction, database) => {
200
+ console.log(`Processing database: ${database}`);
201
+ await transaction.request().query`
202
+ UPDATE Settings SET LastProcessed = GETDATE()
203
+ `;
204
+ });
205
+ ```
206
+
207
+ ### Output Strategies
208
+
209
+ Squilo provides multiple output formats:
210
+
211
+ ```ts
212
+ // JSON output for data export
213
+ import { JsonOutputStrategy } from "squilo/output/strategies";
214
+ await AllActiveDatabases
215
+ .Retrieve(async (transaction) => {
216
+ const result = await transaction.request().query`
217
+ SELECT Id, Name, Status FROM Reports
218
+ `;
219
+ return result.recordset;
220
+ })
221
+ .Output(JsonOutputStrategy());
222
+
223
+ // Excel output for reports
224
+ import { XlsOutputStrategy } from "squilo/output/strategies";
225
+ await Production
226
+ .Connect("ReportsDatabase")
227
+ .Retrieve(async (transaction) => {
228
+ const result = await transaction.request().query`
229
+ SELECT * FROM MonthlyStats WHERE Year = 2024
230
+ `;
231
+ return result.recordset;
232
+ })
233
+ .Output(XlsOutputStrategy());
234
+
235
+ // Console output for debugging
236
+ import { ConsoleOutputStrategy } from "squilo/output/strategies";
237
+ await Production
238
+ .Connect("LogsDatabase")
239
+ .Retrieve(async (transaction) => {
240
+ const result = await transaction.request().query`
241
+ SELECT TOP 10 * FROM ErrorLogs ORDER BY Timestamp DESC
242
+ `;
243
+ return result.recordset;
244
+ })
245
+ .Output(ConsoleOutputStrategy());
246
+
247
+ // Merge results from multiple databases
248
+ import { MergeOutputStrategy } from "squilo/output/strategies";
249
+ await AllActiveDatabases
250
+ .Retrieve(async (transaction, database) => {
251
+ const result = await transaction.request().query`
252
+ SELECT '${database}' as DatabaseName, COUNT(*) as UserCount
253
+ FROM Users WHERE Active = 1
254
+ `;
255
+ return result.recordset;
256
+ })
257
+ .Output(MergeOutputStrategy());
258
+ ```
259
+
260
+ ### Connection Management
261
+
262
+ Properly manage connections in production scripts:
263
+
264
+ ```ts
265
+ // Always close connections in production scripts
266
+ import { Production } from "../servers/production";
267
+
268
+ await Production
269
+ .Connect("ProductionDatabase")
270
+ .Retrieve(async (transaction) => {
271
+ const result = await transaction.request().query`
272
+ SELECT * FROM ImportantData
273
+ `;
274
+ return result.recordset;
275
+ })
276
+ .Output(JsonOutputStrategy());
277
+
278
+ // Close the connection and exit
279
+ await Production.Close();
280
+ process.exit(0);
281
+ ```
282
+
283
+ ### Script Organization
284
+
285
+ Organize your scripts in a clear structure:
286
+
287
+ ```
288
+ project/
289
+ ├── servers/
290
+ │ ├── production.ts # Production server config
291
+ │ └── qas.ts # QAS/staging server config
292
+ ├── databases/
293
+ │ ├── all-active.ts # Dynamic database connections
294
+ │ └── test.ts # Test database connections
295
+ └── scripts/
296
+ ├── reports/ # Report generation scripts
297
+ ├── maintenance/ # Database maintenance scripts
298
+ └── analytics/ # Analytics and data processing
299
+ ```
300
+
301
+ ### Running Scripts
302
+
303
+ ```bash
304
+ # Run individual scripts
305
+ bun run scripts/reports/user-activity.ts
306
+
307
+ # Run with specific environment
308
+ NODE_ENV=production bun run scripts/maintenance/cleanup.ts
309
+ ```
81
310
 
82
311
  ## Reference
83
312
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "squilo",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -14,9 +14,13 @@
14
14
  "name": "Douglas da Silva Sousa",
15
15
  "email": "douglass.sousa@outlook.com.br"
16
16
  },
17
+ "files": [
18
+ "src/**/*"
19
+ ],
17
20
  "exports": {
18
21
  ".": "./src/index.ts",
19
- "./auth": "./src/pipes/auth/strategies/index.ts"
22
+ "./auth/strategies": "./src/pipes/auth/strategies/index.ts",
23
+ "./output/strategies": "./src/pipes/output/strategies/index.ts"
20
24
  },
21
25
  "scripts": {
22
26
  "test": "bun test",
@@ -24,21 +28,22 @@
24
28
  "test:debug": "bun test --inspect"
25
29
  },
26
30
  "devDependencies": {
27
- "@biomejs/biome": "2.2.0",
28
- "@faker-js/faker": "^9.9.0",
31
+ "@biomejs/biome": "2.2.6",
32
+ "@faker-js/faker": "^10.1.0",
29
33
  "@types/bun": "latest",
30
- "@types/xlsx": "^0.0.36",
31
- "testcontainers": "^11.5.1"
34
+ "@types/cli-progress": "^3.11.6",
35
+ "testcontainers": "^11.7.1"
32
36
  },
33
37
  "peerDependencies": {
34
- "typescript": "^5"
38
+ "typescript": "^5.9.3"
35
39
  },
36
40
  "dependencies": {
37
- "@azure/msal-node": "^3.7.1",
38
- "@types/mssql": "^9.1.7",
39
- "mssql": "^11.0.1",
41
+ "@azure/msal-node": "^3.8.0",
42
+ "@types/mssql": "^9.1.8",
43
+ "cli-progress": "^3.12.0",
44
+ "mssql": "^12.0.0",
40
45
  "open": "^10.2.0",
41
- "xlsx": "^0.18.5"
46
+ "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
42
47
  },
43
48
  "license": "MIT"
44
- }
49
+ }
package/src/.DS_Store ADDED
Binary file
package/src/index.ts CHANGED
@@ -1 +1,3 @@
1
- export * from "./pipes/server"
1
+ export * from "./pipes"
2
+ export * from "./pipes/types"
3
+ export * as SQL from "mssql";
@@ -1,17 +1,7 @@
1
- import type { config } from 'mssql';
2
1
  import type { ServerConfig } from "../server/types";
3
- import type { ConnectionOptions } from "../connect";
4
2
  import { Pool } from "../../pool";
5
- import { type ConnectionChain, Connect } from "../connect";
6
-
7
- export type AuthStrategy = (config: ServerConfig) => config;
8
-
9
- export type AuthenticationChain = {
10
- Connect(database: string): ConnectionChain;
11
- Connect(databases: string[], concurrent?: number): ConnectionChain;
12
- Connect(options: ConnectionOptions, concurrent?: number): ConnectionChain;
13
- Close(): Promise<void>;
14
- }
3
+ import { Connect } from "../connect";
4
+ import type { AuthStrategy, AuthenticationChain } from "./types";
15
5
 
16
6
  export const Auth = (config: ServerConfig) => (strategy: AuthStrategy): AuthenticationChain => {
17
7
  const configWithAuth = strategy(config);
@@ -12,7 +12,7 @@ import {
12
12
 
13
13
  import * as path from "path";
14
14
  import type { ServerConfig } from "../../server/types";
15
- import type { AuthStrategy } from "..";
15
+ import type { AuthStrategy } from "../types";
16
16
  import { cwd } from "process";
17
17
 
18
18
  const SCOPES = ["https://database.windows.net//.default"];
@@ -80,12 +80,19 @@ export const GetToken = async (config: NodeAuthOptions) => {
80
80
  let result: AuthenticationResult | null;
81
81
 
82
82
  if (accounts.length > 0 && accounts[0]) {
83
- result = await pca.acquireTokenSilent({
84
- scopes: SCOPES,
85
- account: accounts[0],
86
- });
87
-
88
- return result?.accessToken;
83
+ try {
84
+ result = await pca.acquireTokenSilent({
85
+ scopes: SCOPES,
86
+ account: accounts[0],
87
+ });
88
+
89
+ return result?.accessToken;
90
+ } catch (error) {
91
+ if (error instanceof Error) {
92
+ console.error('Silent token acquisition failed:', error.message);
93
+ }
94
+ console.log("Proceeding to interactive authentication");
95
+ }
89
96
  }
90
97
 
91
98
  const interactiveRequest: InteractiveRequest = {
@@ -1,4 +1,4 @@
1
- import type { AuthStrategy } from ".."
1
+ import type { AuthStrategy } from "../types"
2
2
  import type { ServerConfig } from "../../server/types"
3
3
 
4
4
  export const UserAndPassword = (username: string, password: string): AuthStrategy => (config: ServerConfig) => {
@@ -0,0 +1,12 @@
1
+ import type { config } from "mssql";
2
+ import type { ConnectionChain, ConnectionOptions } from "../connect/types";
3
+ import type { ServerConfig } from "../server/types";
4
+
5
+ export type AuthStrategy = (config: ServerConfig) => config;
6
+
7
+ export type AuthenticationChain = {
8
+ Connect(database: string): ConnectionChain;
9
+ Connect(databases: string[], concurrent?: number): ConnectionChain;
10
+ Connect(options: ConnectionOptions, concurrent?: number): ConnectionChain;
11
+ Close(): Promise<void>;
12
+ }
@@ -1,48 +1,31 @@
1
- import type { ConnectionPool, Transaction } from "mssql";
2
1
  import type { Pool } from "../../pool";
3
- import { type InputChain, Input } from "../input";
2
+ import { Input } from "../input";
4
3
  import { Execute } from "../execute";
5
- import { type RetrieveChain, Retrieve } from "../retrieve";
4
+ import { Retrieve } from "../retrieve";
6
5
 
7
- export type ConnectOverloads = {
8
- (database: string): ConnectionChain;
9
- (databases: string[], concurrent?: number): ConnectionChain;
10
- (options: ConnectionOptions, concurrent?: number): ConnectionChain;
11
- }
6
+ import type { ConnectOverloads, ConnectionOptions, ConnectionChain, DatabaseConnection } from "./types";
12
7
 
13
- export type ConnectionOptions = {
14
- database: string;
15
- query: `SELECT${string}FROM${string}`;
16
- }
8
+ export const Connect = (pool: Pool): ConnectOverloads => (param: string | string[] | ConnectionOptions, concurrent?: number): ConnectionChain => {
9
+ process.on("exit", async () => {
10
+ await pool.closeAll();
11
+ });
17
12
 
18
- export type DatabaseConnection = {
19
- database: string;
20
- connection: Promise<ConnectionPool>;
21
- }
22
-
23
- export type ConnectionChain = {
24
- Execute(fn: (transaction: Transaction, database: string) => Promise<void>): Promise<void>;
25
- Retrieve<TResult>(fn: (transaction: Transaction, database: string) => Promise<TResult>): RetrieveChain<TResult>;
26
- Input<TParam>(fn: () => TParam): InputChain<TParam>;
27
- }
28
-
29
- export const Connect = (pool: Pool): ConnectOverloads => (param: string | string[] | ConnectionOptions, concurrent?: number): ConnectionChain => {
30
- let connections$: AsyncGenerator<DatabaseConnection[]>;
13
+ let connections$: (databases: string[]) => Generator<DatabaseConnection[]>;
31
14
  let databases$: Promise<string[]>;
32
15
 
33
- async function *connections(databases$: Promise<string[]>, concurrent: number = Number.MAX_VALUE) {
34
- const databases = await databases$;
35
-
36
- const databases_result_chunks = Array.from(
37
- {length: Math.ceil(databases.length / concurrent)},
38
- (_, i) => databases.slice(i * concurrent, (i + 1) * concurrent)
39
- );
40
-
41
- for (const databases_result_chunk of databases_result_chunks) {
42
- yield databases_result_chunk.map(database => ({
43
- database,
44
- connection: pool.connect({ database })
45
- }));
16
+ function connections(concurrent: number = Number.MAX_VALUE): (databases: string[]) => Generator<DatabaseConnection[]> {
17
+ return function *(databases: string[]) {
18
+ const databases_result_chunks = Array.from(
19
+ {length: Math.ceil(databases.length / concurrent)},
20
+ (_, i) => databases.slice(i * concurrent, (i + 1) * concurrent)
21
+ );
22
+
23
+ for (const databases_result_chunk of databases_result_chunks) {
24
+ yield databases_result_chunk.map(database => ({
25
+ database,
26
+ connection: pool.connect({ database })
27
+ }));
28
+ }
46
29
  }
47
30
  }
48
31
 
@@ -68,11 +51,11 @@ export const Connect = (pool: Pool): ConnectOverloads => (param: string | string
68
51
  throw new Error("Invalid parameter");
69
52
  }
70
53
 
71
- connections$ = connections(databases$, concurrent);
54
+ connections$ = connections(concurrent);
72
55
 
73
56
  return {
74
- Input: Input(connections$),
75
- Execute: Execute(connections$, null),
76
- Retrieve: Retrieve(connections$, null)
57
+ Input: Input(connections$, databases$),
58
+ Execute: Execute(connections$, databases$, null),
59
+ Retrieve: Retrieve(connections$, databases$, null)
77
60
  }
78
61
  }
@@ -0,0 +1,25 @@
1
+ import type { ConnectionPool, Transaction } from "mssql";
2
+ import type { InputChain } from "../input/types";
3
+ import type { RetrieveChain } from "../retrieve/types";
4
+
5
+ export type ConnectOverloads = {
6
+ (database: string): ConnectionChain;
7
+ (databases: string[], concurrent?: number): ConnectionChain;
8
+ (options: ConnectionOptions, concurrent?: number): ConnectionChain;
9
+ }
10
+
11
+ export type ConnectionOptions = {
12
+ database: string;
13
+ query: `SELECT${string}FROM${string}`;
14
+ }
15
+
16
+ export type DatabaseConnection = {
17
+ database: string;
18
+ connection: Promise<ConnectionPool>;
19
+ }
20
+
21
+ export type ConnectionChain = {
22
+ Execute(fn: (transaction: Transaction, database: string) => Promise<void>): Promise<void>;
23
+ Retrieve<TResult>(fn: (transaction: Transaction, database: string) => Promise<TResult>): RetrieveChain<TResult>;
24
+ Input<TParam>(fn: () => TParam): InputChain<TParam>;
25
+ }
@@ -1,13 +1,25 @@
1
1
  import type { Transaction } from 'mssql';
2
- import type { DatabaseConnection } from "../connect";
2
+ import type { DatabaseConnection } from "../connect/types";
3
+ import { Presets, SingleBar } from 'cli-progress';
4
+ import { AppendError, CleanErrors, type ErrorType } from '../../utils/append-error';
5
+ import { LoadEnv } from '../../utils/load-env';
6
+
7
+ const ENV = LoadEnv();
8
+ let ERRORS_COUNT = 0;
9
+
3
10
 
4
11
  export const Execute = <TParam>(
5
- connections$: AsyncGenerator<DatabaseConnection[]>,
12
+ connections$: (databases: string[]) => Generator<DatabaseConnection[]>,
13
+ databases$: Promise<string[]>,
6
14
  input: TParam
7
15
  ) => {
8
16
  return async (
9
17
  fn: (transaction: Transaction, database: string, params: TParam) => Promise<void>
10
18
  ): Promise<void> => {
19
+ const singlerBar = new SingleBar({
20
+ format: `{bar} {percentage}% | {value}/{total} | {database}`
21
+ }, Presets.shades_classic);
22
+
11
23
  const executeFn = async (dc: DatabaseConnection): Promise<void> => {
12
24
  const opened = await dc.connection;
13
25
  const transaction = opened.transaction()
@@ -15,15 +27,35 @@ export const Execute = <TParam>(
15
27
  await transaction.begin();
16
28
  await fn(transaction, dc.database, input);
17
29
  await transaction.commit();
18
- }
30
+ if (Bun.env.NODE_ENV !== 'test') {
31
+ singlerBar.increment(1, { database: dc.database });
32
+ }
33
+ }
19
34
  catch (error) {
20
35
  await transaction.rollback();
36
+ await AppendError(dc.database, error as ErrorType);
37
+
38
+ if (++ERRORS_COUNT > ENV.MAX_ERRORS) {
39
+ console.error('Max errors reached, exiting...');
40
+ process.exit(1);
41
+ }
21
42
  }
22
43
  };
23
44
 
24
- for await (const connectionBatch of connections$) {
45
+ await CleanErrors();
46
+ const databases = await databases$;
47
+
48
+ if (Bun.env.NODE_ENV !== 'test') {
49
+ singlerBar.start(databases.length, 0);
50
+ }
51
+
52
+ for await (const connectionBatch of connections$(databases)) {
25
53
  const executions = connectionBatch.map(executeFn);
26
54
  await Promise.allSettled(executions);
27
55
  }
56
+
57
+ if (Bun.env.NODE_ENV !== 'test') {
58
+ singlerBar.stop();
59
+ }
28
60
  };
29
61
  };
@@ -1 +1 @@
1
- export { Server } from './server/index'
1
+ export { Server } from './server/index'