squilo 0.1.2 → 0.2.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 (43) hide show
  1. package/README.md +265 -36
  2. package/package.json +18 -13
  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 +15 -8
  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 +28 -15
  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 +43 -29
  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/shared/transaction-runner.ts +58 -0
  28. package/src/pipes/types.ts +1 -0
  29. package/src/pool/index.ts +16 -10
  30. package/src/utils/append-error.spec.ts +100 -0
  31. package/src/utils/append-error.ts +73 -0
  32. package/src/utils/load-env.ts +18 -0
  33. package/biome.json +0 -34
  34. package/test/connect.spec.ts +0 -119
  35. package/test/container/container.spec.ts +0 -33
  36. package/test/container/container.ts +0 -22
  37. package/test/container/setup/databases.spec.ts +0 -24
  38. package/test/container/setup/databases.ts +0 -77
  39. package/test/container/setup/users.spec.ts +0 -25
  40. package/test/container/setup/users.ts +0 -54
  41. package/test/index.spec.ts +0 -68
  42. package/test/input.spec.ts +0 -64
  43. package/tsconfig.json +0 -28
@@ -0,0 +1,73 @@
1
+ import type { FileSink } from "bun";
2
+ import { ConnectionError, PreparedStatementError, RequestError, TransactionError } from "mssql";
3
+
4
+ export type ErrorType = Error | ConnectionError | TransactionError | RequestError | PreparedStatementError;
5
+
6
+ let customLogPath: string | null = null;
7
+
8
+ export const SetLogPath = (path: string) => {
9
+ customLogPath = path;
10
+ if (logWriter) {
11
+ logWriter.end();
12
+ logWriter = null;
13
+ }
14
+ }
15
+
16
+ const getLogPath = () => {
17
+ if (customLogPath) return customLogPath;
18
+ const base = process.argv[1]?.replace(/\.(?:js|ts)/, '');
19
+ return `${base}-last-errors.json`;
20
+ }
21
+
22
+ export const CleanErrors = async () => {
23
+ if (logWriter) {
24
+ logWriter.end();
25
+ logWriter = null;
26
+ }
27
+ const path = getLogPath();
28
+ if (await Bun.file(path).exists()) {
29
+ await Bun.file(path).delete()
30
+ }
31
+ }
32
+
33
+ let logWriter: FileSink | null = null;
34
+
35
+ const getWriter = () => {
36
+ if (!logWriter) {
37
+ logWriter = Bun.file(getLogPath()).writer();
38
+ }
39
+ return logWriter;
40
+ }
41
+
42
+ export const GetErrors = async () => {
43
+ const writer = getWriter();
44
+ writer.end();
45
+ logWriter = null;
46
+
47
+ return Bun.file(getLogPath())
48
+ .text()
49
+ .then(text => text.split('\n').filter(line => line !== ''))
50
+ .then(lines => lines.map(line => JSON.parse(line)));
51
+ }
52
+
53
+ export const AppendError = (database: string, error: ErrorType) => {
54
+ const writer = getWriter();
55
+
56
+ const content = {
57
+ database,
58
+ error: {
59
+ name: error.name,
60
+ message: error.message,
61
+ stack: error.stack,
62
+ code: (error as any).code || undefined,
63
+ number: (error as any).number || undefined,
64
+ state: (error as any).state || undefined,
65
+ class: (error as any).class || undefined,
66
+ serverName: (error as any).serverName || undefined,
67
+ procName: (error as any).procName || undefined,
68
+ lineNumber: (error as any).lineNumber || undefined
69
+ }
70
+ };
71
+
72
+ writer.write(JSON.stringify(content) + '\n');
73
+ }
@@ -0,0 +1,18 @@
1
+ type Env = {
2
+ MAX_ERRORS: number;
3
+ }
4
+
5
+ type StringEnv = {
6
+ [P in keyof Env]?: string
7
+ }
8
+
9
+ declare module "bun" {
10
+ interface Env extends StringEnv {}
11
+ }
12
+
13
+ export const LoadEnv = (): Env => {
14
+ const MAX_ERRORS = Number.parseInt(Bun.env.MAX_ERRORS || '1', 10);
15
+ return {
16
+ MAX_ERRORS
17
+ }
18
+ }
package/biome.json DELETED
@@ -1,34 +0,0 @@
1
- {
2
- "$schema": "https://biomejs.dev/schemas/2.2.0/schema.json",
3
- "vcs": {
4
- "enabled": false,
5
- "clientKind": "git",
6
- "useIgnoreFile": false
7
- },
8
- "files": {
9
- "ignoreUnknown": false
10
- },
11
- "formatter": {
12
- "enabled": true,
13
- "indentStyle": "tab"
14
- },
15
- "linter": {
16
- "enabled": true,
17
- "rules": {
18
- "recommended": true
19
- }
20
- },
21
- "javascript": {
22
- "formatter": {
23
- "quoteStyle": "double"
24
- }
25
- },
26
- "assist": {
27
- "enabled": true,
28
- "actions": {
29
- "source": {
30
- "organizeImports": "on"
31
- }
32
- }
33
- }
34
- }
@@ -1,119 +0,0 @@
1
- import { beforeAll, describe, expect, test, mock, afterEach} from 'bun:test'
2
- import { AzureSqlEdge, SQL_PASSWORD } from './container/container'
3
- import { Server } from '../src';
4
- import { UserAndPassword } from '../src/pipes/auth/strategies';
5
- import { CLIENTS_MANAGER_DATABASE, DATABASES, SetupClientManager, SetupDatabases } from './container/setup/databases';
6
- import { type DatabaseConnection } from '../src/pipes/connect';
7
- import type { Transaction } from 'mssql';
8
-
9
- const mockExecute = mock(<TParam>(connections$: AsyncGenerator<DatabaseConnection[]>) => (fn: (transaction: Transaction, database: string, params: TParam) => Promise<void>) => Promise<void>);
10
- mock.module('../src/pipes/execute', () => ({
11
- Execute: mockExecute,
12
- }))
13
-
14
- describe('Connection overloads', async () => {
15
- const container = await AzureSqlEdge();
16
- const LocalServer = Server({
17
- server: container.getHost(),
18
- port: container.getMappedPort(1433),
19
- options: {
20
- encrypt: false
21
- }
22
- }).Auth(UserAndPassword("sa", SQL_PASSWORD));
23
-
24
- beforeAll(async () => {
25
- await SetupDatabases(container);
26
- await SetupClientManager(container);
27
- });
28
-
29
- afterEach(() => {
30
- mockExecute.mockClear();
31
- })
32
-
33
- test('Connect to unique database', async () => {
34
- const database = DATABASES[0]!;
35
- const Connection = LocalServer.Connect(database);
36
-
37
- expect(Connection).toBeDefined();
38
- expect(mockExecute).toHaveBeenCalledTimes(1);
39
-
40
- const connectionsGenerator = mockExecute.mock.calls[0]![0];
41
- const uniqueConnection = await connectionsGenerator.next();
42
-
43
- expect(uniqueConnection.done).toBe(false);
44
- expect(uniqueConnection.value).toHaveLength(1);
45
- expect(uniqueConnection.value).toEqual([{
46
- database,
47
- connection: expect.any(Promise),
48
- }]);
49
- });
50
-
51
- test('Connect with database list', async () => {
52
- const databases = [DATABASES[0]!, DATABASES[1]!];
53
- LocalServer.Connect(databases);
54
-
55
- expect(mockExecute).toHaveBeenCalledTimes(1);
56
- const connectionsGenerator = mockExecute.mock.calls[0]![0];
57
- const connections = await connectionsGenerator.next();
58
-
59
- expect(connections.done).toBe(false);
60
- expect(connections.value).toHaveLength(2);
61
- expect(connections.value).toEqual([{
62
- database: DATABASES[0]!,
63
- connection: expect.any(Promise),
64
- }, {
65
- database: DATABASES[1]!,
66
- connection: expect.any(Promise),
67
- }]);
68
- });
69
-
70
- test('Connect with limited concurrent database list', async () => {
71
- const databases = [DATABASES[0]!, DATABASES[1]!, DATABASES[2]!, DATABASES[3]!];
72
- LocalServer.Connect(databases, 2);
73
-
74
- expect(mockExecute).toHaveBeenCalledTimes(1);
75
- const connectionsGenerator = mockExecute.mock.calls[0]![0];
76
- const firstConnections = await connectionsGenerator.next();
77
-
78
- expect(firstConnections.done).toBe(false);
79
- expect(firstConnections.value).toHaveLength(2);
80
- expect(firstConnections.value).toEqual([{
81
- database: DATABASES[0]!,
82
- connection: expect.any(Promise),
83
- }, {
84
- database: DATABASES[1]!,
85
- connection: expect.any(Promise),
86
- }]);
87
-
88
- const secondConnections = await connectionsGenerator.next();
89
- expect(secondConnections.done).toBe(false);
90
- expect(secondConnections.value).toHaveLength(2);
91
- expect(secondConnections.value).toEqual([{
92
- database: DATABASES[2]!,
93
- connection: expect.any(Promise),
94
- }, {
95
- database: DATABASES[3]!,
96
- connection: expect.any(Promise),
97
- }]);
98
-
99
- const thirdConnections = await connectionsGenerator.next();
100
- expect(thirdConnections.done).toBe(true);
101
- });
102
-
103
- test('Connect with query', async () => {
104
- LocalServer.Connect({
105
- database: CLIENTS_MANAGER_DATABASE,
106
- query: 'SELECT DatabaseName FROM Clients WHERE Active = 1'
107
- });
108
-
109
- expect(mockExecute).toHaveBeenCalledTimes(1);
110
- const connectionsGenerator = mockExecute.mock.calls[0]![0];
111
- const connections = await connectionsGenerator.next();
112
- expect(connections.done).toBe(false);
113
- expect(connections.value).toHaveLength(5);
114
- expect(connections.value).toEqual(DATABASES.map(database => ({
115
- database,
116
- connection: expect.any(Promise),
117
- })));
118
- })
119
- })
@@ -1,33 +0,0 @@
1
- import { afterAll, beforeAll, describe, expect, it, test } from 'bun:test'
2
- import { connect, ConnectionPool } from 'mssql'
3
- import type { StartedTestContainer } from 'testcontainers'
4
- import { AzureSqlEdge, SQL_PASSWORD } from './container';
5
-
6
- describe('SQL Server', async () => {
7
- let container: StartedTestContainer
8
- let sqlClient: ConnectionPool
9
-
10
- beforeAll(async () => {
11
- container = await AzureSqlEdge()
12
- sqlClient = await connect({
13
- server: container.getHost(),
14
- port: container.getMappedPort(1433),
15
- database: 'master',
16
- user: 'sa',
17
- password: SQL_PASSWORD,
18
- options: {
19
- encrypt: false
20
- }
21
- });
22
- })
23
-
24
- afterAll(async () => {
25
- await sqlClient.close();
26
- await container.stop();
27
- })
28
-
29
- it('should connect to the database', async () => {
30
- const result = await sqlClient.query`SELECT 'Hello'`;
31
- expect(result.recordset[0]['']).toBe('Hello');
32
- })
33
- })
@@ -1,22 +0,0 @@
1
- import { GenericContainer, type StartedTestContainer, Wait } from "testcontainers";
2
-
3
- export const SQL_PASSWORD = "YourStrong@Passw0rd";
4
-
5
- export const AzureSqlEdge = () => new GenericContainer('mcr.microsoft.com/azure-sql-edge')
6
- .withEnvironment({
7
- 'ACCEPT_EULA': 'Y',
8
- 'MSSQL_SA_PASSWORD': SQL_PASSWORD
9
- })
10
- .withExposedPorts(1433)
11
- .withWaitStrategy(Wait.forLogMessage('Recovery is complete'))
12
- .start();
13
-
14
- export const CONFIG = (container: StartedTestContainer) => ({
15
- server: container.getHost(),
16
- port: container.getMappedPort(1433),
17
- user: "sa",
18
- password: SQL_PASSWORD,
19
- options: {
20
- encrypt: false
21
- }
22
- })
@@ -1,24 +0,0 @@
1
- import { beforeAll, describe, expect, it } from "bun:test"
2
- import { SetupDatabases } from "./databases"
3
- import { AzureSqlEdge, CONFIG } from "../container"
4
- import { connect } from "mssql"
5
-
6
- describe('Database creation', async () => {
7
- const container = await AzureSqlEdge();
8
-
9
- beforeAll(async () => {
10
- await SetupDatabases(container);
11
- })
12
-
13
- it('Should create 5 databases', async () => {
14
- const conn = await connect({
15
- ...CONFIG(container),
16
- database: "master"
17
- });
18
-
19
- const result = await conn.query`SELECT * FROM sys.databases WHERE name like 'TestDB%'`
20
- expect(result.recordset).toHaveLength(5);
21
-
22
- await conn.close();
23
- })
24
- })
@@ -1,77 +0,0 @@
1
- import { Bit, connect, NVarChar, Table } from "mssql";
2
- import { CONFIG } from "../container";
3
- import type { StartedTestContainer } from "testcontainers";
4
-
5
- export const CLIENTS_MANAGER_DATABASE = 'ClientsManager';
6
-
7
- export const DATABASES = [
8
- "TestDB1",
9
- "TestDB2",
10
- "TestDB3",
11
- "TestDB4",
12
- "TestDB5",
13
- ];
14
-
15
- export const SetupClientManager = async (container: StartedTestContainer) => {
16
- const masterConn = await connect({
17
- ...CONFIG(container),
18
- database: "master"
19
- });
20
-
21
- await masterConn.query(`
22
- IF NOT EXISTS (SELECT * FROM sys.databases WHERE name = '${CLIENTS_MANAGER_DATABASE}')
23
- BEGIN
24
- CREATE DATABASE ${CLIENTS_MANAGER_DATABASE};
25
- END
26
- `);
27
-
28
- await masterConn.close();
29
-
30
- const clientManagerConn = await connect({
31
- ...CONFIG(container),
32
- database: CLIENTS_MANAGER_DATABASE
33
- });
34
-
35
- await clientManagerConn.query(`
36
- IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'Clients')
37
- BEGIN
38
- CREATE TABLE Clients (
39
- Id INT PRIMARY KEY IDENTITY(1,1),
40
- Name NVARCHAR(255) UNIQUE NOT NULL,
41
- DatabaseName NVARCHAR(255) UNIQUE NOT NULL,
42
- Active BIT NOT NULL
43
- );
44
- END
45
- `);
46
-
47
- const table = new Table("Clients");
48
- table.columns.add("Name", NVarChar(255), { nullable: false});
49
- table.columns.add("DatabaseName", NVarChar(255), { nullable: false});
50
- table.columns.add("Active", Bit(), { nullable: false});
51
-
52
- for (const database of DATABASES) {
53
- table.rows.add(database, database, 1);
54
- }
55
- table.rows.add("TestDB6", "TestDB6", 0);
56
-
57
- await clientManagerConn.request().bulk(table);
58
- await clientManagerConn.close();
59
- }
60
-
61
- export const SetupDatabases = async (container: StartedTestContainer) => {
62
- const conn = await connect({
63
- ...CONFIG(container),
64
- database: "master"
65
- });
66
-
67
- for (const dbName of DATABASES) {
68
- await conn.query(`
69
- IF NOT EXISTS (SELECT * FROM sys.databases WHERE name = '${dbName}')
70
- BEGIN
71
- CREATE DATABASE ${dbName};
72
- END
73
- `);
74
- }
75
-
76
- await conn.close();
77
- }
@@ -1,25 +0,0 @@
1
- import { beforeAll, describe, expect, test } from "bun:test";
2
- import { AzureSqlEdge, CONFIG } from "../container";
3
- import { DATABASES, SetupDatabases } from "./databases";
4
- import { SetupUsers } from "./users";
5
- import { connect } from "mssql";
6
-
7
- describe('Users table', async () => {
8
- const container = await AzureSqlEdge();
9
-
10
- beforeAll(async () => {
11
- await SetupDatabases(container);
12
- await SetupUsers(container);
13
- })
14
-
15
- test.each(DATABASES)('Should create 10 users in %s', async (database) => {
16
- const conn = await connect({
17
- ...CONFIG(container),
18
- database
19
- });
20
- const result = await conn.query`SELECT * FROM Users`
21
- expect(result.recordset).toHaveLength(10);
22
-
23
- await conn.close();
24
- })
25
- })
@@ -1,54 +0,0 @@
1
- import { DATABASES } from "./databases";
2
- import { connect, NVarChar, Table } from "mssql";
3
- import { CONFIG } from "../container";
4
- import { faker } from "@faker-js/faker";
5
- import type { StartedTestContainer } from "testcontainers";
6
-
7
- export type User = {
8
- Id: number;
9
- Name: string;
10
- Email: string;
11
- };
12
-
13
- export const SetupUsers = async (container: StartedTestContainer, options: {
14
- populate: boolean;
15
- } = {
16
- populate: true
17
- }) => {
18
- for (const database of DATABASES) {
19
- const conn = await connect({
20
- ...CONFIG(container),
21
- database
22
- });
23
-
24
- await conn.request().query`
25
- IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'Users')
26
- BEGIN
27
- CREATE TABLE Users (
28
- Id INT PRIMARY KEY IDENTITY(1,1),
29
- Name NVARCHAR(100) NOT NULL,
30
- Email NVARCHAR(100) NOT NULL
31
- );
32
- END
33
- `;
34
-
35
- if (options.populate) {
36
- await conn.request().query(`DELETE FROM Users`);
37
-
38
- const usersTable = new Table("Users");
39
- usersTable.columns.add("Name", NVarChar(100), { nullable: false });
40
- usersTable.columns.add("Email", NVarChar(100), { nullable: false });
41
-
42
- for (let i = 0; i < 10; i++) {
43
- const name = faker.person.fullName({ firstName: "Joe"});
44
- const email = faker.internet.email({ firstName: "Joe" }) + " ";
45
-
46
- usersTable.rows.add(name, email);
47
- }
48
-
49
- await conn.request().bulk(usersTable);
50
- }
51
-
52
- await conn.close();
53
- }
54
- }
@@ -1,68 +0,0 @@
1
- import { expect, beforeAll, describe, it, afterAll, test } from "bun:test";
2
- import { Server } from "../src/pipes/server";
3
- import { UserAndPassword } from "../src/pipes/auth/strategies";
4
- import { MergeOutputStrategy } from "../src/pipes/output/strategies";
5
- import { AzureSqlEdge, SQL_PASSWORD } from "./container/container";
6
- import { DATABASES, SetupDatabases } from "./container/setup/databases";
7
- import { SetupUsers, type User } from "./container/setup/users";
8
-
9
- describe('Squilo test', async () => {
10
- const container = await AzureSqlEdge();
11
-
12
- const LocalServer = Server({
13
- server: container.getHost(),
14
- port: container.getMappedPort(1433),
15
- options: {
16
- encrypt: false
17
- },
18
- })
19
- .Auth(UserAndPassword("sa", SQL_PASSWORD));
20
-
21
- beforeAll(async () => {
22
- await SetupDatabases(container);
23
- await SetupUsers(container);
24
- })
25
-
26
- afterAll(async () => {
27
- await LocalServer.Close();
28
- })
29
-
30
- it("Get one from each database", async () => {
31
-
32
- const users = await LocalServer
33
- .Connect(DATABASES)
34
- .Retrieve(async (transaction) => {
35
- const result = await transaction.request().query<User>`
36
- SELECT TOP 1 * FROM Users
37
- `;
38
-
39
- return result.recordset;
40
- })
41
- .Output(MergeOutputStrategy());
42
-
43
- expect(users).toHaveLength(5);
44
- });
45
-
46
- test('Should fix user\'s email that are ending with extra space', async () => {
47
- await LocalServer
48
- .Connect(DATABASES)
49
- .Execute(async (transaction) => {
50
- await transaction.request().query`
51
- UPDATE Users SET Email = RTRIM(Email)
52
- `;
53
- })
54
-
55
- const users = await LocalServer
56
- .Connect(DATABASES)
57
- .Retrieve(async (transaction) => {
58
- const result = await transaction.request().query<User>`
59
- SELECT * FROM Users
60
- `;
61
-
62
- return result.recordset;
63
- })
64
- .Output(MergeOutputStrategy());
65
-
66
- expect(users.every((user) => user.Email.endsWith(" ") === false)).toBe(true);
67
- })
68
- });
@@ -1,64 +0,0 @@
1
- import { afterAll, beforeAll, describe, expect, test } from "bun:test";
2
- import { AzureSqlEdge, SQL_PASSWORD } from "./container/container";
3
- import { Server } from "../src/pipes/server";
4
- import { UserAndPassword } from "../src/pipes/auth/strategies";
5
- import { DATABASES, SetupDatabases } from "./container/setup/databases";
6
- import { SetupUsers, type User } from "./container/setup/users";
7
- import { MergeOutputStrategy } from "../src/pipes/output/strategies";
8
-
9
- describe('Squilo input test', async () => {
10
- const container = await AzureSqlEdge();
11
- const localServer = Server({
12
- server: container.getHost(),
13
- port: container.getMappedPort(1433),
14
- options: {
15
- encrypt: false
16
- },
17
- })
18
- .Auth(UserAndPassword("sa", SQL_PASSWORD));
19
-
20
- beforeAll(async () => {
21
- await SetupDatabases(container);
22
- await SetupUsers(container, { populate: false })
23
- })
24
-
25
- afterAll(async () => {
26
- await localServer.Close();
27
- })
28
-
29
- test('Should add a user using console input', async () => {
30
- let name: string | null = "joe";
31
- let email: string | null = "joe.doe@example.com";
32
-
33
- await localServer
34
- .Connect(DATABASES)
35
- .Input<Omit<User, "Id">>(() => {
36
- return {
37
- Name: name!,
38
- Email: email!,
39
- }
40
- })
41
- .Execute(async (transaction, _, user) => {
42
- await transaction.request().query`
43
- INSERT INTO Users (Name, Email)
44
- VALUES (${user.Name}, ${user.Email})
45
- `;
46
- });
47
-
48
- const users = await localServer
49
- .Connect(DATABASES)
50
- .Retrieve(async (transaction) => {
51
- const result = await transaction.request().query<User>`
52
- SELECT * FROM Users
53
- `;
54
-
55
- return result.recordset;
56
- })
57
- .Output(MergeOutputStrategy());
58
-
59
- expect(users).toHaveLength(5);
60
-
61
- const everyUserIsJoe = users.every(user => user.Name === name && user.Email === email);
62
- expect(everyUserIsJoe).toBe(true);
63
- })
64
- })
package/tsconfig.json DELETED
@@ -1,28 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- // Environment setup & latest features
4
- "lib": ["ESNext"],
5
- "target": "ESNext",
6
- "module": "Preserve",
7
- "moduleDetection": "force",
8
- "allowJs": true,
9
-
10
- // Bundler mode
11
- "moduleResolution": "bundler",
12
- "allowImportingTsExtensions": true,
13
- "verbatimModuleSyntax": true,
14
- "noEmit": true,
15
-
16
- // Best practices
17
- "strict": true,
18
- "skipLibCheck": true,
19
- "noFallthroughCasesInSwitch": true,
20
- "noUncheckedIndexedAccess": true,
21
- "noImplicitOverride": true,
22
-
23
- // Some stricter flags (disabled by default)
24
- "noUnusedLocals": false,
25
- "noUnusedParameters": false,
26
- "noPropertyAccessFromIndexSignature": false
27
- }
28
- }