From ce1b55bebd292358870410cf5cb89aa13b7066c0 Mon Sep 17 00:00:00 2001 From: Misha Kaletsky <15040698+mmkal@users.noreply.github.com> Date: Fri, 20 Sep 2024 11:26:18 -0400 Subject: [PATCH] pgkit package, pgkit.config.ts, passing tests (#433) Co-authored-by: Misha Kaletsky --- .eslintrc.js | 44 - .github/workflows/main.yml | 14 +- apps/docs/pages/packages/client.md | 443 +++++----- apps/docs/vercel.json | 3 + package.json | 4 +- packages/admin/package.json | 2 +- packages/admin/src/client/views/Table.tsx | 1 + packages/admin/src/server/migrations.ts | 8 +- packages/admin/test/__snapshots__/seed.json | 158 ++-- packages/admin/test/autocomplete.test.ts | 2 +- packages/client/eslint.config.js | 5 + packages/client/readme.md | 439 +++++----- packages/client/src/client.ts | 24 +- packages/client/src/errors.ts | 40 +- packages/client/src/types.ts | 6 + .../test/__snapshots__/slonik37.test.ts.snap | 4 +- packages/client/test/api-usage.test.ts | 155 ++-- packages/client/test/errors.test.ts | 181 ++-- packages/client/test/pg-promise-usage.test.ts | 10 + packages/client/test/recipes.test.ts | 76 +- packages/client/test/slonik37.test.ts | 4 +- packages/client/test/snapshots.ts | 39 +- packages/client/test/zod.test.ts | 55 +- packages/migra/package.json | 2 +- packages/migra/src/changes.ts | 10 +- packages/migra/src/command.ts | 62 +- packages/migra/src/index.ts | 2 +- packages/migra/src/migra.ts | 3 + packages/migra/test/fixtures.ts | 19 +- packages/migra/test/limitations.test.ts | 29 + packages/migra/test/python-parity.test.ts | 2 +- packages/migrator/package.json | 4 +- packages/migrator/src/cli.ts | 59 +- packages/migrator/src/migrator.ts | 58 +- packages/migrator/src/types.ts | 8 +- packages/pgkit/package.json | 14 +- packages/pgkit/src/config.ts | 24 +- packages/pgkit/src/router.ts | 18 +- packages/schemainspect/src/pg/obj.ts | 28 +- .../test/__snapshots__/collations.json | 22 +- .../test/__snapshots__/everything.json | 120 +-- packages/schemainspect/test/json.test.ts | 10 +- packages/typegen/package.json | 4 +- packages/typegen/src/generate.ts | 33 +- packages/typegen/src/query/column-info.ts | 2 +- packages/typegen/src/util.ts | 24 +- packages/typegen/src/utils/memoize.ts | 5 +- packages/typegen/src/write/index.ts | 12 +- packages/typegen/src/write/inline.ts | 1 + packages/typegen/src/write/prettify.ts | 42 +- packages/typegen/test/helper.ts | 8 +- packages/typegen/test/limitations.test.ts | 4 +- packages/typegen/test/options.test.ts | 4 +- packages/typegen/test/primitives.test.ts | 18 +- packages/typegen/test/type-mappings.test.ts | 4 +- packages/typegen/test/ugly.test.ts | 5 +- packages/typegen/test/watch.test.ts | 272 +++--- pnpm-lock.yaml | 823 ++---------------- tools/npmono/package.json | 2 +- turbo.json | 4 +- 60 files changed, 1548 insertions(+), 1930 deletions(-) delete mode 100644 .eslintrc.js create mode 100644 apps/docs/vercel.json create mode 100644 packages/migra/test/limitations.test.ts diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index db535f5c..00000000 --- a/.eslintrc.js +++ /dev/null @@ -1,44 +0,0 @@ -module.exports = { - parser: '@typescript-eslint/parser', - parserOptions: {ecmaVersion: 2018, sourceType: 'module'}, - plugins: ['@typescript-eslint/eslint-plugin', 'prettier', 'codegen', 'import'], - extends: ['plugin:import/recommended', 'plugin:import/typescript'], - ignorePatterns: ['**/fixtures/**', '**/dist/**'], - rules: { - 'prettier/prettier': ['warn', require('./.prettierrc')], - '@typescript-eslint/prefer-namespace-keyword': 'warn', - '@typescript-eslint/no-namespace': ['warn', {allowDeclarations: true}], - 'codegen/codegen': 'warn', - 'import/no-extraneous-dependencies': 'error', - // seems to do the wrong thing with find-up - 'import/namespace': 'off', - eqeqeq: 'error', - yoda: 'error', - }, - overrides: [ - { - files: ['*/*/test/**', '**/*.test.ts'], - rules: { - // allow using root package dependencies in tests - 'import/no-extraneous-dependencies': 'off', - 'import/no-unresolved': 'off', - }, - }, - { - files: ['**/*.js'], - rules: { - // js files are usually build scripts etc., not published. No problem if they use hoisting. - 'import/no-extraneous-dependencies': 'off', - }, - }, - { - files: ['**/*.md', '*.md'], - rules: { - 'prettier/prettier': 'off', - 'no-trailing-spaces': 'off', - 'no-multiple-empty-lines': 'off', - 'unicorn/filename-case': 'off', - }, - }, - ], -} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a5229a99..9fa93273 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest services: postgres: - image: postgres:12 + image: postgres:13 env: PGPORT: '5432' POSTGRES_USER: postgres @@ -36,6 +36,18 @@ jobs: run: | echo 'select 123 as test_column \gdesc' | docker exec -i postgres_github_actions psql "postgresql://postgres:postgres@localhost:5432/postgres" -f - | grep 'test_column | integer' - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.x + - name: Install python dependencies (for testing migra) + run: | + python -m pip install --upgrade pip + pip install setuptools + pip install migra + pip install psycopg2-binary + migra --help | grep 'Generate a database migration' + migra EMPTY postgresql://postgres:postgres@localhost:5432/postgres --unsafe || echo 'migra exited with an error code - check output' - uses: actions/setup-node@v2 with: node-version: ${{ matrix.node-version }} diff --git a/apps/docs/pages/packages/client.md b/apps/docs/pages/packages/client.md index 8e03ac33..13e68db0 100644 --- a/apps/docs/pages/packages/client.md +++ b/apps/docs/pages/packages/client.md @@ -29,12 +29,12 @@ The API design is based on [slonik](https://npmjs.com/package/slonik) - which re @pgkit/client is the basis for these libraries: - + - [@pgkit/typegen](/packages/typegen) - Automatically generates typescript types from SQL queries - [@pgkit/schemainspect](./packages/schemainspect) - SQL Schema Inspection for PostgreSQL - [@pgkit/migra](/packages/migra) - A CLI to generate PostgeSQL schema diff scripts -- [@pgkit/admin](/packages/admin) - A zero-config PostgeSQL admin server, with schema inspection and autocomplete. - [@pgkit/migrator](/packages/migrator) - PostgeSQL migration tool +- [@pgkit/admin](/packages/admin) - A zero-config PostgeSQL admin server, with schema inspection and autocomplete. Note that @pgkit/migra and @pgkit/schemainspect are pure ports of their Python equivalents. They are fantastically useful, and hopefully more and more can be built on top of them in the future. @@ -351,7 +351,7 @@ const result = await client.oneFirst(sql` select '2000-01-01T12:00:00Z'::timestamptz + ${sql.interval({days: 1, hours: 1})} as ts `) expect(result).toBeInstanceOf(Date) -expect(result).toMatchInlineSnapshot(`2000-01-02T13:00:00.000Z`) +expect(result).toMatchInlineSnapshot(`"2000-01-02T13:00:00.000Z"`) const interval = await client.oneFirst(sql`select ${sql.interval({days: 1})}`) expect(interval).toMatchInlineSnapshot(`"1 day"`) @@ -365,7 +365,7 @@ Pass a buffer value from JavaScript to PostgreSQL. const result = await client.oneFirst(sql` select ${sql.binary(Buffer.from('hello'))} as b `) -expect(result).toMatchInlineSnapshot(`"\\x68656c6c6f"`) +expect(result).toMatchInlineSnapshot(`"\\\\x68656c6c6f"`) ``` ### sql.json @@ -478,39 +478,30 @@ await expect(client.any(sql.type(StringId)`select id::text from usage_test`)).re const error = await client.any(sql.type(StringId)`select id from usage_test`).catch(e => e) -expect(error.cause).toMatchInlineSnapshot(` +expect(error).toMatchInlineSnapshot(` + [QueryError]: [select-usage_test_8729cac]: Parsing rows failed { - "error": [ZodError: [ - { - "code": "invalid_type", - "expected": "string", - "received": "number", - "path": [ - "id" - ], - "message": "Expected string, received number" - } - ]], - "message": "[ - { - "code": "invalid_type", - "expected": "string", - "received": "number", - "path": [ - "id" - ], - "message": "Expected string, received number" - } - ]", - "name": "QueryErrorCause", + "message": "[select-usage_test_8729cac]: Parsing rows failed", "query": { "name": "select-usage_test_8729cac", - "parse": [Function], "sql": "select id from usage_test", - "templateArgs": [Function], "token": "sql", - "values": [], + "values": [] }, + "cause": { + "name": "ZodError", + "issues": [ + { + "code": "invalid_type", + "expected": "string", + "received": "number", + "path": [ + "id" + ], + "message": "Expected string, received number" + } + ] + } } `) ``` @@ -522,18 +513,22 @@ Wrap the query function to customize the error message ```typescript client = createClient(client.connectionString(), { ...client.options, - wrapQueryFn: query => { + pgpOptions: { + ...client.options.pgpOptions, + connect: { + ...client.options.pgpOptions?.connect, + application_name: 'impatient', + }, + }, + wrapQueryFn: queryFn => { const parentWrapper = client.options.wrapQueryFn || (x => x) return async (...args) => { - const parentQueryFn = parentWrapper(query) + const parentQueryFn = parentWrapper(queryFn) try { return await parentQueryFn(...args) } catch (e) { - if (e instanceof QueryError && e.message.endsWith('Parsing rows failed')) { - throw new QueryError(e.message, { - query: e.cause.query, - error: fromError(e.cause.error), - }) + if (e instanceof QueryError && isZodErrorLike(e.cause)) { + e.cause = fromError(e.cause) } throw e } @@ -544,19 +539,45 @@ const StringId = z.object({id: z.string()}) const error = await client.any(sql.type(StringId)`select id from usage_test`).catch(e => e) -expect(error.cause).toMatchInlineSnapshot(` +expect(error).toMatchInlineSnapshot(` + [QueryError]: [select-usage_test_8729cac]: Parsing rows failed { - "error": [ZodValidationError: Validation error: Expected string, received number at "id"], - "message": "Validation error: Expected string, received number at "id"", - "name": "QueryErrorCause", + "message": "[select-usage_test_8729cac]: Parsing rows failed", "query": { "name": "select-usage_test_8729cac", - "parse": [Function], "sql": "select id from usage_test", - "templateArgs": [Function], "token": "sql", - "values": [], + "values": [] }, + "cause": { + "name": "ZodValidationError", + "message": "Validation error: Expected string, received number at \\"id\\"", + "cause": { + "name": "ZodError", + "issues": [ + { + "code": "invalid_type", + "expected": "string", + "received": "number", + "path": [ + "id" + ], + "message": "Expected string, received number" + } + ] + }, + "details": [ + { + "code": "invalid_type", + "expected": "string", + "received": "number", + "path": [ + "id" + ], + "message": "Expected string, received number" + } + ] + } } `) ``` @@ -579,19 +600,45 @@ expectTypeOf(result).toEqualTypeOf<{name: string}>() expect(result).toEqual({name: 'Bob'}) const err = await client.any(sql.typeAlias('Profile')`select 123 as name`).catch(e => e) -expect(err.cause).toMatchInlineSnapshot(` +expect(err).toMatchInlineSnapshot(` + [QueryError]: [select_245d49b]: Parsing rows failed { - "error": [ZodValidationError: Validation error: Expected string, received number at "name"], - "message": "Validation error: Expected string, received number at "name"", - "name": "QueryErrorCause", + "message": "[select_245d49b]: Parsing rows failed", "query": { "name": "select_245d49b", - "parse": [Function], "sql": "select 123 as name", - "templateArgs": [Function], "token": "sql", - "values": [], + "values": [] }, + "cause": { + "name": "ZodValidationError", + "message": "Validation error: Expected string, received number at \\"name\\"", + "cause": { + "name": "ZodError", + "issues": [ + { + "code": "invalid_type", + "expected": "string", + "received": "number", + "path": [ + "name" + ], + "message": "Expected string, received number" + } + ] + }, + "details": [ + { + "code": "invalid_type", + "expected": "string", + "received": "number", + "path": [ + "name" + ], + "message": "Expected string, received number" + } + ] + } } `) ``` @@ -769,36 +816,33 @@ const getResult = () => await expect(getResult()).rejects.toMatchInlineSnapshot(` { + "message": "[select-zod_test_83bbed1]: Parsing rows failed", + "query": { + "name": "select-zod_test_83bbed1", + "sql": "\\n select * from zod_test\\n ", + "token": "sql", + "values": [] + }, "cause": { - "query": { - "name": "select-zod_test_83bbed1", - "sql": "\\n select * from zod_test\\n ", - "token": "sql", - "values": [] - }, - "error": { - "issues": [ - { - "code": "invalid_type", - "expected": "string", - "received": "undefined", - "path": [ - "name" - ], - "message": "Required" - }, - { - "code": "custom", - "message": "id must be even", - "path": [ - "id" - ] - } - ], - "name": "ZodError" - }, - "message": "[\\n {\\n \\"code\\": \\"invalid_type\\",\\n \\"expected\\": \\"string\\",\\n \\"received\\": \\"undefined\\",\\n \\"path\\": [\\n \\"name\\"\\n ],\\n \\"message\\": \\"Required\\"\\n },\\n {\\n \\"code\\": \\"custom\\",\\n \\"message\\": \\"id must be even\\",\\n \\"path\\": [\\n \\"id\\"\\n ]\\n }\\n]", - "name": "QueryErrorCause" + "issues": [ + { + "code": "invalid_type", + "expected": "string", + "received": "undefined", + "path": [ + "name" + ], + "message": "Required" + }, + { + "code": "custom", + "message": "id must be even", + "path": [ + "id" + ] + } + ], + "name": "ZodError" } } `) @@ -853,6 +897,11 @@ expect(sqlProduced).toMatchInlineSnapshot(` // Simplistic way of logging query times. For more accurate results, use process.hrtime() const log = vi.fn() const client = createClient('postgresql://postgres:postgres@localhost:5432/postgres', { + pgpOptions: { + connect: { + application_name: 'query-logger', + }, + }, wrapQueryFn: queryFn => async query => { const start = Date.now() const result = await queryFn(query) @@ -907,18 +956,18 @@ expect(log.mock.calls[0][0]).toMatchInlineSnapshot( "fields": [ { "name": "id", - "tableID": 123456789, + "tableID": "[tableID]", "columnID": 1, - "dataTypeID": 123456789, + "dataTypeID": "[dataTypeID]", "dataTypeSize": 4, "dataTypeModifier": -1, "format": "text" }, { "name": "name", - "tableID": 123456789, + "tableID": "[tableID]", "columnID": 2, - "dataTypeID": 123456789, + "dataTypeID": "[dataTypeID]", "dataTypeSize": -1, "dataTypeModifier": -1, "format": "text" @@ -952,22 +1001,21 @@ const patient = createClient(client.connectionString() + '?longTimeout', { const sleepSeconds = (shortTimeoutMs * 2) / 1000 await expect(impatient.one(sql`select pg_sleep(${sleepSeconds})`)).rejects.toThrowErrorMatchingInlineSnapshot( ` - [[Query select_9dcc021]: Query read timeout] + [QueryError]: [select_9dcc021]: Executing query failed { + "message": "[select_9dcc021]: Executing query failed", + "query": { + "name": "select_9dcc021", + "sql": "select pg_sleep($1)", + "token": "sql", + "values": [ + 0.04 + ] + }, "cause": { - "query": { - "name": "select_9dcc021", - "sql": "select pg_sleep($1)", - "token": "sql", - "values": [ - 0.04 - ] - }, - "error": { - "query": "select pg_sleep(0.04)" - }, + "name": "Error", "message": "Query read timeout", - "name": "QueryErrorCause" + "query": "select pg_sleep(0.04)" } } `, @@ -987,6 +1035,7 @@ const impatientClient = createClient(client.connectionString() + '?shortTimeout' pgpOptions: { connect: { query_timeout: shortTimeoutMs, + application_name: 'impatient', }, }, }) @@ -994,11 +1043,17 @@ const patientClient = createClient(client.connectionString() + '?longTimeout', { pgpOptions: { connect: { query_timeout: shortTimeoutMs * 3, + application_name: 'patient', }, }, }) const appClient = createClient(client.connectionString(), { + pgpOptions: { + connect: { + application_name: 'app', + }, + }, wrapQueryFn: _queryFn => { return async query => { let clientToUse = patientClient @@ -1026,22 +1081,21 @@ await expect( select pg_sleep(${sleepSeconds}) `), ).rejects.toThrowErrorMatchingInlineSnapshot(` - [[Query select_6289211]: Query read timeout] + [QueryError]: [select_6289211]: Executing query failed { + "message": "[select_6289211]: Executing query failed", + "query": { + "name": "select_6289211", + "sql": "\\n select pg_sleep($1)\\n ", + "token": "sql", + "values": [ + 0.04 + ] + }, "cause": { - "query": { - "name": "select_6289211", - "sql": "\\n select pg_sleep($1)\\n ", - "token": "sql", - "values": [ - 0.04 - ] - }, - "error": { - "query": "\\n select pg_sleep(0.04)\\n " - }, + "name": "Error", "message": "Query read timeout", - "name": "QueryErrorCause" + "query": "\\n select pg_sleep(0.04)\\n " } } `) @@ -1178,52 +1232,48 @@ For errors based on the number of rows returned (for `one`, `oneFirst`, `many`, ```typescript await expect(pool.one(sql`select * from test_errors where id > 1`)).rejects.toMatchInlineSnapshot( ` - [[Query select-test_errors_36f5f64]: Expected one row] + [QueryError]: [select-test_errors_36f5f64]: Expected one row { - "message": "[Query select-test_errors_36f5f64]: Expected one row", - "cause": { - "query": { - "name": "select-test_errors_36f5f64", - "sql": "select * from test_errors where id > 1", - "token": "sql", - "values": [] - }, - "result": { - "rows": [ - { - "id": 2, - "name": "two" - }, - { - "id": 3, - "name": "three" - } - ], - "command": "SELECT", - "rowCount": 2, - "fields": [ - { - "name": "id", - "tableID": 123456789, - "columnID": 1, - "dataTypeID": 123456789, - "dataTypeSize": 4, - "dataTypeModifier": -1, - "format": "text" - }, - { - "name": "name", - "tableID": 123456789, - "columnID": 2, - "dataTypeID": 123456789, - "dataTypeSize": -1, - "dataTypeModifier": -1, - "format": "text" - } - ] - }, - "message": "", - "name": "QueryErrorCause" + "message": "[select-test_errors_36f5f64]: Expected one row", + "query": { + "name": "select-test_errors_36f5f64", + "sql": "select * from test_errors where id > 1", + "token": "sql", + "values": [] + }, + "result": { + "rows": [ + { + "id": 2, + "name": "two" + }, + { + "id": 3, + "name": "three" + } + ], + "command": "SELECT", + "rowCount": 2, + "fields": [ + { + "name": "id", + "tableID": "[tableID]", + "columnID": 1, + "dataTypeID": "[dataTypeID]", + "dataTypeSize": 4, + "dataTypeModifier": -1, + "format": "text" + }, + { + "name": "name", + "tableID": "[tableID]", + "columnID": 2, + "dataTypeID": "[dataTypeID]", + "dataTypeSize": -1, + "dataTypeModifier": -1, + "format": "text" + } + ] } } `, @@ -1233,11 +1283,11 @@ await expect(pool.one(sql`select * from test_errors where id > 1`)).rejects.toMa ##### maybeOne error ```typescript -await expect(pool.maybeOne(sql`select * from test_errors where id > 1`)).rejects.toMatchInlineSnapshot(` - [[Query select-test_errors_36f5f64]: Expected at most one row] - { - "message": "[Query select-test_errors_36f5f64]: Expected at most one row", - "cause": { +await expect(pool.maybeOne(sql`select * from test_errors where id > 1`)).rejects.toMatchInlineSnapshot( + ` + [QueryError]: [select-test_errors_36f5f64]: Expected at most one row + { + "message": "[select-test_errors_36f5f64]: Expected at most one row", "query": { "name": "select-test_errors_36f5f64", "sql": "select * from test_errors where id > 1", @@ -1260,39 +1310,37 @@ await expect(pool.maybeOne(sql`select * from test_errors where id > 1`)).rejects "fields": [ { "name": "id", - "tableID": 123456789, + "tableID": "[tableID]", "columnID": 1, - "dataTypeID": 123456789, + "dataTypeID": "[dataTypeID]", "dataTypeSize": 4, "dataTypeModifier": -1, "format": "text" }, { "name": "name", - "tableID": 123456789, + "tableID": "[tableID]", "columnID": 2, - "dataTypeID": 123456789, + "dataTypeID": "[dataTypeID]", "dataTypeSize": -1, "dataTypeModifier": -1, "format": "text" } ] - }, - "message": "", - "name": "QueryErrorCause" + } } - } -`) + `, +) ``` ##### many error ```typescript -await expect(pool.many(sql`select * from test_errors where id > 100`)).rejects.toMatchInlineSnapshot(` - [[Query select-test_errors_34cad85]: Expected at least one row] - { - "message": "[Query select-test_errors_34cad85]: Expected at least one row", - "cause": { +await expect(pool.many(sql`select * from test_errors where id > 100`)).rejects.toMatchInlineSnapshot( + ` + [QueryError]: [select-test_errors_34cad85]: Expected at least one row + { + "message": "[select-test_errors_34cad85]: Expected at least one row", "query": { "name": "select-test_errors_34cad85", "sql": "select * from test_errors where id > 100", @@ -1306,71 +1354,68 @@ await expect(pool.many(sql`select * from test_errors where id > 100`)).rejects.t "fields": [ { "name": "id", - "tableID": 123456789, + "tableID": "[tableID]", "columnID": 1, - "dataTypeID": 123456789, + "dataTypeID": "[dataTypeID]", "dataTypeSize": 4, "dataTypeModifier": -1, "format": "text" }, { "name": "name", - "tableID": 123456789, + "tableID": "[tableID]", "columnID": 2, - "dataTypeID": 123456789, + "dataTypeID": "[dataTypeID]", "dataTypeSize": -1, "dataTypeModifier": -1, "format": "text" } ] - }, - "message": "", - "name": "QueryErrorCause" + } } - } -`) + `, +) ``` ##### syntax error ```typescript -await expect(pool.query(sql`select * frooom test_errors`)).rejects.toMatchInlineSnapshot(` - [[Query select_fb83277]: syntax error at or near "frooom"] - { - "message": "[Query select_fb83277]: syntax error at or near \\"frooom\\"", - "cause": { +await expect(pool.query(sql`select * frooom test_errors`)).rejects.toMatchInlineSnapshot( + ` + [QueryError]: [select_fb83277]: Executing query failed (syntax_error) + { + "message": "[select_fb83277]: Executing query failed (syntax_error)", "query": { "name": "select_fb83277", "sql": "select * frooom test_errors", "token": "sql", "values": [] }, - "error": { - "length": 95, + "cause": { "name": "error", + "message": "syntax error at or near \\"frooom\\"", + "length": 95, "severity": "ERROR", "code": "42601", "position": "10", "file": "scan.l", - "line": "123456789", + "line": "[line]", "routine": "scanner_yyerror", "query": "select * frooom test_errors" - }, - "message": "syntax error at or near \\"frooom\\"", - "name": "QueryErrorCause" + } } - } -`) + `, +) const err: Error = await pool.query(sql`select * frooom test_errors`).catch(e => e) expect(err.stack).toMatchInlineSnapshot(` - Error: [Query select_fb83277]: syntax error at or near "frooom" + Error: [select_fb83277]: Executing query failed (syntax_error) at Object.query (/packages/client/src/client.ts::) at /packages/client/test/errors.test.ts:: `) -expect((err as QueryError).cause?.error?.stack).toMatchInlineSnapshot(` +expect((err as any).cause?.stack).toMatchInlineSnapshot(` error: syntax error at or near "frooom" at Parser.parseErrorMessage (/node_modules/.pnpm/pg-protocol@1.6.0/node_modules/pg-protocol/src/parser.ts::) at Parser.handlePacket (/node_modules/.pnpm/pg-protocol@1.6.0/node_modules/pg-protocol/src/parser.ts::) diff --git a/apps/docs/vercel.json b/apps/docs/vercel.json new file mode 100644 index 00000000..7b3d050d --- /dev/null +++ b/apps/docs/vercel.json @@ -0,0 +1,3 @@ +{ + "buildCommand": "next build" +} \ No newline at end of file diff --git a/package.json b/package.json index 906a68c8..d1b6ceba 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,9 @@ "semver": "^7.6.0", "sort-package-json": "^2.8.0", "tsx": "^4.19.0", - "turbo": "^1.12.3", + "turbo": "^2.1.2", "type-fest": "^4.10.3", - "typescript": ">=3.0.0", + "typescript": "^5.6.2", "util": "^0.12.3" } } diff --git a/packages/admin/package.json b/packages/admin/package.json index 4607cba8..43ba137d 100644 --- a/packages/admin/package.json +++ b/packages/admin/package.json @@ -106,7 +106,7 @@ "strip-indent": "^4.0.0", "tailwindcss": "^3.4.3", "tsx": "^4.19.0", - "typescript": "^5.2.2", + "typescript": "^5.6.2", "typescript-eslint": "^7.1.0", "vite": "^5.0.0", "vitest": "^1.2.2" diff --git a/packages/admin/src/client/views/Table.tsx b/packages/admin/src/client/views/Table.tsx index 4f34d739..9b365233 100644 --- a/packages/admin/src/client/views/Table.tsx +++ b/packages/admin/src/client/views/Table.tsx @@ -35,6 +35,7 @@ export const Table = ({identifier}: {identifier: string}) => { const nextEnabled = Boolean(limit) && values.length >= limit const tableInfo = inspected?.tables[identifier] + // todo: dedupe columns const columnNames = Object.values(tableInfo?.columns || {}).map(c => c.name) const [whereClause, setWhereClause] = React.useState('') const [columns, setColumns] = React.useState(['*']) diff --git a/packages/admin/src/server/migrations.ts b/packages/admin/src/server/migrations.ts index 519b515b..40b055af 100644 --- a/packages/admin/src/server/migrations.ts +++ b/packages/admin/src/server/migrations.ts @@ -17,11 +17,12 @@ const migratorProcedure = trpc.procedure .use(async ({next, input}) => { return next({ ctx: { + // todo: get from ctx migrator: migrator, - confirm: async (checkSql: string) => { + confirm: async (checkSql: string): Promise => { checkSql = checkSql.trim() if (!checkSql) { - return false + return null } const confirmation = input?.confirmation?.trim() @@ -30,7 +31,8 @@ const migratorProcedure = trpc.procedure throw new Error('confirmation_missing:' + checkSql) } - return checkSql === confirmation + // todo: allow updating the confirmation string + return checkSql === confirmation ? null : confirmation }, }, }) diff --git a/packages/admin/test/__snapshots__/seed.json b/packages/admin/test/__snapshots__/seed.json index ab418112..a5608592 100644 --- a/packages/admin/test/__snapshots__/seed.json +++ b/packages/admin/test/__snapshots__/seed.json @@ -29,7 +29,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "name": { @@ -45,7 +45,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "another_column": { @@ -61,7 +61,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -90,7 +90,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "name": { @@ -106,7 +106,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -135,7 +135,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "description": { @@ -151,7 +151,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "status": { @@ -167,7 +167,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -232,7 +232,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "order_id": { @@ -248,7 +248,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "quantity": { @@ -264,7 +264,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -293,7 +293,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "shipping_address": { @@ -309,7 +309,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "status": { @@ -333,7 +333,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "status2": { @@ -349,7 +349,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "h": { @@ -365,7 +365,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -394,7 +394,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "name": { @@ -410,7 +410,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "price": { @@ -426,7 +426,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "x": { @@ -442,7 +442,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "newcolumn": { @@ -458,7 +458,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "newcolumn2": { @@ -474,7 +474,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -505,7 +505,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "b": { @@ -521,7 +521,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -552,7 +552,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "b": { @@ -568,7 +568,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -599,7 +599,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "name": { @@ -615,7 +615,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "another_column": { @@ -631,7 +631,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -660,7 +660,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "name": { @@ -676,7 +676,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -705,7 +705,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "description": { @@ -721,7 +721,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "status": { @@ -737,7 +737,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -802,7 +802,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "order_id": { @@ -818,7 +818,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "quantity": { @@ -834,7 +834,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -863,7 +863,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "shipping_address": { @@ -879,7 +879,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "status": { @@ -903,7 +903,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "status2": { @@ -919,7 +919,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "h": { @@ -935,7 +935,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -964,7 +964,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "name": { @@ -980,7 +980,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "price": { @@ -996,7 +996,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "x": { @@ -1012,7 +1012,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "newcolumn": { @@ -1028,7 +1028,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "newcolumn2": { @@ -1044,7 +1044,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -1073,7 +1073,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "b": { @@ -1089,7 +1089,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -1118,7 +1118,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "b": { @@ -1134,7 +1134,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -1237,7 +1237,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "name": { @@ -1253,7 +1253,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "another_column": { @@ -1269,7 +1269,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -1298,7 +1298,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "name": { @@ -1314,7 +1314,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -1343,7 +1343,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "description": { @@ -1359,7 +1359,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "status": { @@ -1375,7 +1375,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -1440,7 +1440,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "order_id": { @@ -1456,7 +1456,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "quantity": { @@ -1472,7 +1472,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -1501,7 +1501,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "shipping_address": { @@ -1517,7 +1517,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "status": { @@ -1541,7 +1541,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "status2": { @@ -1557,7 +1557,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "h": { @@ -1573,7 +1573,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -1602,7 +1602,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "name": { @@ -1618,7 +1618,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "price": { @@ -1634,7 +1634,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "x": { @@ -1650,7 +1650,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "newcolumn": { @@ -1666,7 +1666,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "newcolumn2": { @@ -1682,7 +1682,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -1711,7 +1711,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "b": { @@ -1727,7 +1727,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -1756,7 +1756,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "b": { @@ -1772,7 +1772,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -1950,7 +1950,7 @@ "hstore": { "name": "hstore", "schema": "public", - "version": "1.6" + "version": "1.7" } }, "functions": { diff --git a/packages/admin/test/autocomplete.test.ts b/packages/admin/test/autocomplete.test.ts index bb3d94e3..87c11007 100644 --- a/packages/admin/test/autocomplete.test.ts +++ b/packages/admin/test/autocomplete.test.ts @@ -296,7 +296,7 @@ test('suggest table', async () => { test('suggest table w schema qualifier', async () => { // todo: improve this - expect(suggest('select * from public.pr|', {debug: true})).toMatchInlineSnapshot(`[]`) + expect(suggest('select * from public.pr|')).toMatchInlineSnapshot(`[]`) }) test('suggest tables w/ name clash', async () => { diff --git a/packages/client/eslint.config.js b/packages/client/eslint.config.js index e0a1cd0f..5ee33aca 100644 --- a/packages/client/eslint.config.js +++ b/packages/client/eslint.config.js @@ -1,6 +1,11 @@ module.exports = [ ...require('eslint-plugin-mmkal').recommendedFlatConfigs, {ignores: ['**/ignoreme/**']}, + { + rules: { + '@typescript-eslint/no-namespace': 'off', + }, + }, { files: ['*.md/*'], rules: { diff --git a/packages/client/readme.md b/packages/client/readme.md index bb5aff4c..5dfa6d75 100644 --- a/packages/client/readme.md +++ b/packages/client/readme.md @@ -352,7 +352,7 @@ const result = await client.oneFirst(sql` select '2000-01-01T12:00:00Z'::timestamptz + ${sql.interval({days: 1, hours: 1})} as ts `) expect(result).toBeInstanceOf(Date) -expect(result).toMatchInlineSnapshot(`2000-01-02T13:00:00.000Z`) +expect(result).toMatchInlineSnapshot(`"2000-01-02T13:00:00.000Z"`) const interval = await client.oneFirst(sql`select ${sql.interval({days: 1})}`) expect(interval).toMatchInlineSnapshot(`"1 day"`) @@ -366,7 +366,7 @@ Pass a buffer value from JavaScript to PostgreSQL. const result = await client.oneFirst(sql` select ${sql.binary(Buffer.from('hello'))} as b `) -expect(result).toMatchInlineSnapshot(`"\\x68656c6c6f"`) +expect(result).toMatchInlineSnapshot(`"\\\\x68656c6c6f"`) ``` ### sql.json @@ -479,39 +479,30 @@ await expect(client.any(sql.type(StringId)`select id::text from usage_test`)).re const error = await client.any(sql.type(StringId)`select id from usage_test`).catch(e => e) -expect(error.cause).toMatchInlineSnapshot(` +expect(error).toMatchInlineSnapshot(` + [QueryError]: [select-usage_test_8729cac]: Parsing rows failed { - "error": [ZodError: [ - { - "code": "invalid_type", - "expected": "string", - "received": "number", - "path": [ - "id" - ], - "message": "Expected string, received number" - } - ]], - "message": "[ - { - "code": "invalid_type", - "expected": "string", - "received": "number", - "path": [ - "id" - ], - "message": "Expected string, received number" - } - ]", - "name": "QueryErrorCause", + "message": "[select-usage_test_8729cac]: Parsing rows failed", "query": { "name": "select-usage_test_8729cac", - "parse": [Function], "sql": "select id from usage_test", - "templateArgs": [Function], "token": "sql", - "values": [], + "values": [] }, + "cause": { + "name": "ZodError", + "issues": [ + { + "code": "invalid_type", + "expected": "string", + "received": "number", + "path": [ + "id" + ], + "message": "Expected string, received number" + } + ] + } } `) ``` @@ -523,18 +514,22 @@ Wrap the query function to customize the error message ```typescript client = createClient(client.connectionString(), { ...client.options, - wrapQueryFn: query => { + pgpOptions: { + ...client.options.pgpOptions, + connect: { + ...client.options.pgpOptions?.connect, + application_name: 'impatient', + }, + }, + wrapQueryFn: queryFn => { const parentWrapper = client.options.wrapQueryFn || (x => x) return async (...args) => { - const parentQueryFn = parentWrapper(query) + const parentQueryFn = parentWrapper(queryFn) try { return await parentQueryFn(...args) } catch (e) { - if (e instanceof QueryError && e.message.endsWith('Parsing rows failed')) { - throw new QueryError(e.message, { - query: e.cause.query, - error: fromError(e.cause.error), - }) + if (e instanceof QueryError && isZodErrorLike(e.cause)) { + e.cause = fromError(e.cause) } throw e } @@ -545,19 +540,45 @@ const StringId = z.object({id: z.string()}) const error = await client.any(sql.type(StringId)`select id from usage_test`).catch(e => e) -expect(error.cause).toMatchInlineSnapshot(` +expect(error).toMatchInlineSnapshot(` + [QueryError]: [select-usage_test_8729cac]: Parsing rows failed { - "error": [ZodValidationError: Validation error: Expected string, received number at "id"], - "message": "Validation error: Expected string, received number at "id"", - "name": "QueryErrorCause", + "message": "[select-usage_test_8729cac]: Parsing rows failed", "query": { "name": "select-usage_test_8729cac", - "parse": [Function], "sql": "select id from usage_test", - "templateArgs": [Function], "token": "sql", - "values": [], + "values": [] }, + "cause": { + "name": "ZodValidationError", + "message": "Validation error: Expected string, received number at \\"id\\"", + "cause": { + "name": "ZodError", + "issues": [ + { + "code": "invalid_type", + "expected": "string", + "received": "number", + "path": [ + "id" + ], + "message": "Expected string, received number" + } + ] + }, + "details": [ + { + "code": "invalid_type", + "expected": "string", + "received": "number", + "path": [ + "id" + ], + "message": "Expected string, received number" + } + ] + } } `) ``` @@ -580,19 +601,45 @@ expectTypeOf(result).toEqualTypeOf<{name: string}>() expect(result).toEqual({name: 'Bob'}) const err = await client.any(sql.typeAlias('Profile')`select 123 as name`).catch(e => e) -expect(err.cause).toMatchInlineSnapshot(` +expect(err).toMatchInlineSnapshot(` + [QueryError]: [select_245d49b]: Parsing rows failed { - "error": [ZodValidationError: Validation error: Expected string, received number at "name"], - "message": "Validation error: Expected string, received number at "name"", - "name": "QueryErrorCause", + "message": "[select_245d49b]: Parsing rows failed", "query": { "name": "select_245d49b", - "parse": [Function], "sql": "select 123 as name", - "templateArgs": [Function], "token": "sql", - "values": [], + "values": [] }, + "cause": { + "name": "ZodValidationError", + "message": "Validation error: Expected string, received number at \\"name\\"", + "cause": { + "name": "ZodError", + "issues": [ + { + "code": "invalid_type", + "expected": "string", + "received": "number", + "path": [ + "name" + ], + "message": "Expected string, received number" + } + ] + }, + "details": [ + { + "code": "invalid_type", + "expected": "string", + "received": "number", + "path": [ + "name" + ], + "message": "Expected string, received number" + } + ] + } } `) ``` @@ -770,36 +817,33 @@ const getResult = () => await expect(getResult()).rejects.toMatchInlineSnapshot(` { + "message": "[select-zod_test_83bbed1]: Parsing rows failed", + "query": { + "name": "select-zod_test_83bbed1", + "sql": "\\n select * from zod_test\\n ", + "token": "sql", + "values": [] + }, "cause": { - "query": { - "name": "select-zod_test_83bbed1", - "sql": "\\n select * from zod_test\\n ", - "token": "sql", - "values": [] - }, - "error": { - "issues": [ - { - "code": "invalid_type", - "expected": "string", - "received": "undefined", - "path": [ - "name" - ], - "message": "Required" - }, - { - "code": "custom", - "message": "id must be even", - "path": [ - "id" - ] - } - ], - "name": "ZodError" - }, - "message": "[\\n {\\n \\"code\\": \\"invalid_type\\",\\n \\"expected\\": \\"string\\",\\n \\"received\\": \\"undefined\\",\\n \\"path\\": [\\n \\"name\\"\\n ],\\n \\"message\\": \\"Required\\"\\n },\\n {\\n \\"code\\": \\"custom\\",\\n \\"message\\": \\"id must be even\\",\\n \\"path\\": [\\n \\"id\\"\\n ]\\n }\\n]", - "name": "QueryErrorCause" + "issues": [ + { + "code": "invalid_type", + "expected": "string", + "received": "undefined", + "path": [ + "name" + ], + "message": "Required" + }, + { + "code": "custom", + "message": "id must be even", + "path": [ + "id" + ] + } + ], + "name": "ZodError" } } `) @@ -854,6 +898,11 @@ expect(sqlProduced).toMatchInlineSnapshot(` // Simplistic way of logging query times. For more accurate results, use process.hrtime() const log = vi.fn() const client = createClient('postgresql://postgres:postgres@localhost:5432/postgres', { + pgpOptions: { + connect: { + application_name: 'query-logger', + }, + }, wrapQueryFn: queryFn => async query => { const start = Date.now() const result = await queryFn(query) @@ -908,18 +957,18 @@ expect(log.mock.calls[0][0]).toMatchInlineSnapshot( "fields": [ { "name": "id", - "tableID": 123456789, + "tableID": "[tableID]", "columnID": 1, - "dataTypeID": 123456789, + "dataTypeID": "[dataTypeID]", "dataTypeSize": 4, "dataTypeModifier": -1, "format": "text" }, { "name": "name", - "tableID": 123456789, + "tableID": "[tableID]", "columnID": 2, - "dataTypeID": 123456789, + "dataTypeID": "[dataTypeID]", "dataTypeSize": -1, "dataTypeModifier": -1, "format": "text" @@ -953,22 +1002,21 @@ const patient = createClient(client.connectionString() + '?longTimeout', { const sleepSeconds = (shortTimeoutMs * 2) / 1000 await expect(impatient.one(sql`select pg_sleep(${sleepSeconds})`)).rejects.toThrowErrorMatchingInlineSnapshot( ` - [[Query select_9dcc021]: Query read timeout] + [QueryError]: [select_9dcc021]: Executing query failed { + "message": "[select_9dcc021]: Executing query failed", + "query": { + "name": "select_9dcc021", + "sql": "select pg_sleep($1)", + "token": "sql", + "values": [ + 0.04 + ] + }, "cause": { - "query": { - "name": "select_9dcc021", - "sql": "select pg_sleep($1)", - "token": "sql", - "values": [ - 0.04 - ] - }, - "error": { - "query": "select pg_sleep(0.04)" - }, + "name": "Error", "message": "Query read timeout", - "name": "QueryErrorCause" + "query": "select pg_sleep(0.04)" } } `, @@ -988,6 +1036,7 @@ const impatientClient = createClient(client.connectionString() + '?shortTimeout' pgpOptions: { connect: { query_timeout: shortTimeoutMs, + application_name: 'impatient', }, }, }) @@ -995,11 +1044,17 @@ const patientClient = createClient(client.connectionString() + '?longTimeout', { pgpOptions: { connect: { query_timeout: shortTimeoutMs * 3, + application_name: 'patient', }, }, }) const appClient = createClient(client.connectionString(), { + pgpOptions: { + connect: { + application_name: 'app', + }, + }, wrapQueryFn: _queryFn => { return async query => { let clientToUse = patientClient @@ -1027,22 +1082,21 @@ await expect( select pg_sleep(${sleepSeconds}) `), ).rejects.toThrowErrorMatchingInlineSnapshot(` - [[Query select_6289211]: Query read timeout] + [QueryError]: [select_6289211]: Executing query failed { + "message": "[select_6289211]: Executing query failed", + "query": { + "name": "select_6289211", + "sql": "\\n select pg_sleep($1)\\n ", + "token": "sql", + "values": [ + 0.04 + ] + }, "cause": { - "query": { - "name": "select_6289211", - "sql": "\\n select pg_sleep($1)\\n ", - "token": "sql", - "values": [ - 0.04 - ] - }, - "error": { - "query": "\\n select pg_sleep(0.04)\\n " - }, + "name": "Error", "message": "Query read timeout", - "name": "QueryErrorCause" + "query": "\\n select pg_sleep(0.04)\\n " } } `) @@ -1179,52 +1233,48 @@ For errors based on the number of rows returned (for `one`, `oneFirst`, `many`, ```typescript await expect(pool.one(sql`select * from test_errors where id > 1`)).rejects.toMatchInlineSnapshot( ` - [[Query select-test_errors_36f5f64]: Expected one row] + [QueryError]: [select-test_errors_36f5f64]: Expected one row { - "message": "[Query select-test_errors_36f5f64]: Expected one row", - "cause": { - "query": { - "name": "select-test_errors_36f5f64", - "sql": "select * from test_errors where id > 1", - "token": "sql", - "values": [] - }, - "result": { - "rows": [ - { - "id": 2, - "name": "two" - }, - { - "id": 3, - "name": "three" - } - ], - "command": "SELECT", - "rowCount": 2, - "fields": [ - { - "name": "id", - "tableID": 123456789, - "columnID": 1, - "dataTypeID": 123456789, - "dataTypeSize": 4, - "dataTypeModifier": -1, - "format": "text" - }, - { - "name": "name", - "tableID": 123456789, - "columnID": 2, - "dataTypeID": 123456789, - "dataTypeSize": -1, - "dataTypeModifier": -1, - "format": "text" - } - ] - }, - "message": "", - "name": "QueryErrorCause" + "message": "[select-test_errors_36f5f64]: Expected one row", + "query": { + "name": "select-test_errors_36f5f64", + "sql": "select * from test_errors where id > 1", + "token": "sql", + "values": [] + }, + "result": { + "rows": [ + { + "id": 2, + "name": "two" + }, + { + "id": 3, + "name": "three" + } + ], + "command": "SELECT", + "rowCount": 2, + "fields": [ + { + "name": "id", + "tableID": "[tableID]", + "columnID": 1, + "dataTypeID": "[dataTypeID]", + "dataTypeSize": 4, + "dataTypeModifier": -1, + "format": "text" + }, + { + "name": "name", + "tableID": "[tableID]", + "columnID": 2, + "dataTypeID": "[dataTypeID]", + "dataTypeSize": -1, + "dataTypeModifier": -1, + "format": "text" + } + ] } } `, @@ -1234,11 +1284,11 @@ await expect(pool.one(sql`select * from test_errors where id > 1`)).rejects.toMa ##### maybeOne error ```typescript -await expect(pool.maybeOne(sql`select * from test_errors where id > 1`)).rejects.toMatchInlineSnapshot(` - [[Query select-test_errors_36f5f64]: Expected at most one row] - { - "message": "[Query select-test_errors_36f5f64]: Expected at most one row", - "cause": { +await expect(pool.maybeOne(sql`select * from test_errors where id > 1`)).rejects.toMatchInlineSnapshot( + ` + [QueryError]: [select-test_errors_36f5f64]: Expected at most one row + { + "message": "[select-test_errors_36f5f64]: Expected at most one row", "query": { "name": "select-test_errors_36f5f64", "sql": "select * from test_errors where id > 1", @@ -1261,39 +1311,37 @@ await expect(pool.maybeOne(sql`select * from test_errors where id > 1`)).rejects "fields": [ { "name": "id", - "tableID": 123456789, + "tableID": "[tableID]", "columnID": 1, - "dataTypeID": 123456789, + "dataTypeID": "[dataTypeID]", "dataTypeSize": 4, "dataTypeModifier": -1, "format": "text" }, { "name": "name", - "tableID": 123456789, + "tableID": "[tableID]", "columnID": 2, - "dataTypeID": 123456789, + "dataTypeID": "[dataTypeID]", "dataTypeSize": -1, "dataTypeModifier": -1, "format": "text" } ] - }, - "message": "", - "name": "QueryErrorCause" + } } - } -`) + `, +) ``` ##### many error ```typescript -await expect(pool.many(sql`select * from test_errors where id > 100`)).rejects.toMatchInlineSnapshot(` - [[Query select-test_errors_34cad85]: Expected at least one row] - { - "message": "[Query select-test_errors_34cad85]: Expected at least one row", - "cause": { +await expect(pool.many(sql`select * from test_errors where id > 100`)).rejects.toMatchInlineSnapshot( + ` + [QueryError]: [select-test_errors_34cad85]: Expected at least one row + { + "message": "[select-test_errors_34cad85]: Expected at least one row", "query": { "name": "select-test_errors_34cad85", "sql": "select * from test_errors where id > 100", @@ -1307,71 +1355,68 @@ await expect(pool.many(sql`select * from test_errors where id > 100`)).rejects.t "fields": [ { "name": "id", - "tableID": 123456789, + "tableID": "[tableID]", "columnID": 1, - "dataTypeID": 123456789, + "dataTypeID": "[dataTypeID]", "dataTypeSize": 4, "dataTypeModifier": -1, "format": "text" }, { "name": "name", - "tableID": 123456789, + "tableID": "[tableID]", "columnID": 2, - "dataTypeID": 123456789, + "dataTypeID": "[dataTypeID]", "dataTypeSize": -1, "dataTypeModifier": -1, "format": "text" } ] - }, - "message": "", - "name": "QueryErrorCause" + } } - } -`) + `, +) ``` ##### syntax error ```typescript -await expect(pool.query(sql`select * frooom test_errors`)).rejects.toMatchInlineSnapshot(` - [[Query select_fb83277]: syntax error at or near "frooom"] - { - "message": "[Query select_fb83277]: syntax error at or near \\"frooom\\"", - "cause": { +await expect(pool.query(sql`select * frooom test_errors`)).rejects.toMatchInlineSnapshot( + ` + [QueryError]: [select_fb83277]: Executing query failed (syntax_error) + { + "message": "[select_fb83277]: Executing query failed (syntax_error)", "query": { "name": "select_fb83277", "sql": "select * frooom test_errors", "token": "sql", "values": [] }, - "error": { - "length": 95, + "cause": { "name": "error", + "message": "syntax error at or near \\"frooom\\"", + "length": 95, "severity": "ERROR", "code": "42601", "position": "10", "file": "scan.l", - "line": "123456789", + "line": "[line]", "routine": "scanner_yyerror", "query": "select * frooom test_errors" - }, - "message": "syntax error at or near \\"frooom\\"", - "name": "QueryErrorCause" + } } - } -`) + `, +) const err: Error = await pool.query(sql`select * frooom test_errors`).catch(e => e) expect(err.stack).toMatchInlineSnapshot(` - Error: [Query select_fb83277]: syntax error at or near "frooom" + Error: [select_fb83277]: Executing query failed (syntax_error) at Object.query (/packages/client/src/client.ts::) at /packages/client/test/errors.test.ts:: `) -expect((err as QueryError).cause?.error?.stack).toMatchInlineSnapshot(` +expect((err as any).cause?.stack).toMatchInlineSnapshot(` error: syntax error at or near "frooom" at Parser.parseErrorMessage (/node_modules/.pnpm/pg-protocol@1.6.0/node_modules/pg-protocol/src/parser.ts::) at Parser.handlePacket (/node_modules/.pnpm/pg-protocol@1.6.0/node_modules/pg-protocol/src/parser.ts::) diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts index 30b7433c..ef163416 100644 --- a/packages/client/src/client.ts +++ b/packages/client/src/client.ts @@ -3,7 +3,17 @@ import TypeOverrides from 'pg/lib/type-overrides' import pgPromise from 'pg-promise' import {QueryError, errorFromUnknown} from './errors' import {applyRecommendedTypeParsers} from './type-parsers' -import {Client, First, Queryable, SQLQueryRowType, ClientOptions, Connection, Transaction, Result} from './types' +import { + Client, + First, + Queryable, + SQLQueryRowType, + ClientOptions, + Connection, + Transaction, + Result, + DriverQueryable, +} from './types' export const identityParser = (input: unknown): T => input as T @@ -58,8 +68,7 @@ const createQueryable = (query: Queryable['query']): Queryable => { } } -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const createQueryFn = (pgpQueryable: pgPromise.ITask | pgPromise.IDatabase): Queryable['query'] => { +export const createQueryFn = (pgpQueryable: DriverQueryable): Queryable['query'] => { return async query => { type Row = SQLQueryRowType let result: Result @@ -73,14 +82,14 @@ export const createQueryFn = (pgpQueryable: pgPromise.ITask | pgPromise.IDa result = {rows, command, rowCount, fields} } catch (err: unknown) { const error = errorFromUnknown(err) - throw new QueryError(error.message, {query, error}) + throw new QueryError('Executing query failed', {query, cause: error}) } try { return {...result, rows: await Promise.all(result.rows.map(query.parse))} } catch (err: unknown) { const error = errorFromUnknown(err) - throw new QueryError(`Parsing rows failed`, {query, error}) + throw new QueryError(`Parsing rows failed`, {query, cause: error}) } } } @@ -129,7 +138,7 @@ export const createClient = (connectionString: string, options: ClientOptions = }) } - const connect: Client['connect'] = async callback => { + const taskMethod: Client['task'] = async callback => { return pgPromiseClient.task({tag: crypto.randomUUID()}, async task => { const connectionInfo: Connection['connectionInfo'] = {pgp: task} const pgSuiteConnection: Connection = { @@ -149,7 +158,8 @@ export const createClient = (connectionString: string, options: ClientOptions = ...createQueryable(createWrappedQueryFn(pgPromiseClient)), connectionString: () => connectionString, end: async () => pgPromiseClient.$pool.end(), - connect, + connect: taskMethod, + task: taskMethod, transaction: transactionFnFromTask(pgPromiseClient), } } diff --git a/packages/client/src/errors.ts b/packages/client/src/errors.ts index 9120cbe8..6f79dd23 100644 --- a/packages/client/src/errors.ts +++ b/packages/client/src/errors.ts @@ -33,28 +33,32 @@ export function errorFromUnknown(err: unknown) { return err instanceof Error ? err : new Error(`Non error thrown: ${String(err)}`, {cause: err}) } -export class QueryErrorCause { - constructor( - readonly query: Pick, 'name'> & Partial>, - readonly error: Error | undefined, - readonly result: {rows: unknown[]} | undefined, - ) {} -} - -export class QueryError extends Error { - cause!: { - name: string - message: string +export namespace QueryError { + export type Params = { query: Pick, 'name'> & Partial> - error?: Error result?: {rows: unknown[]} + cause?: Error + } +} +export class QueryError extends Error { + query: QueryError.Params['query'] + result?: QueryError.Params['result'] + + constructor(message: string, {query, result, cause}: QueryError.Params) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const suffix = cause && (cause as any).code in pgErrorCodes ? ` (${pgErrorCodes[(cause as any).code]})` : '' + super(`[${query.name}]: ${message}${suffix}`, cause ? {cause} : undefined) + this.query = query + this.result = result } - constructor(message: string, cause: Omit) { - super(`[Query ${cause.query.name}]: ${message || cause?.error?.message || cause?.error?.constructor?.name}`, { - cause, - }) - this.cause = {...cause, message: cause?.error?.message || '', name: 'QueryErrorCause'} + toJSON() { + return { + message: this.message, + query: this.query, + result: this.result, + cause: this.cause, + } } } diff --git a/packages/client/src/types.ts b/packages/client/src/types.ts index 84df4389..cc75e0bc 100644 --- a/packages/client/src/types.ts +++ b/packages/client/src/types.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import pgPromise from 'pg-promise' export interface SQLQuery, Values extends unknown[] = unknown[]> { @@ -34,6 +35,10 @@ export interface Result { rowCount: number | null } +export type DriverQueryable = { + result: (query: string, values?: unknown[]) => Promise> +} + export interface Queryable { query(query: SQLQuery): Promise> @@ -69,6 +74,7 @@ export interface Client extends Queryable { connectionString(): string end(): Promise connect(callback: (connection: Connection) => Promise): Promise + task(callback: (connection: Connection) => Promise): Promise transaction(callback: (connection: Transaction) => Promise): Promise } diff --git a/packages/client/test/__snapshots__/slonik37.test.ts.snap b/packages/client/test/__snapshots__/slonik37.test.ts.snap index 21d985f7..0a3c0e18 100644 --- a/packages/client/test/__snapshots__/slonik37.test.ts.snap +++ b/packages/client/test/__snapshots__/slonik37.test.ts.snap @@ -1,6 +1,6 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`createSqlTag + sql.typeAlias 1`] = `undefined`; +exports[`createSqlTag + sql.typeAlias 1`] = `[SchemaValidationError: Query returned rows that do not conform with the schema.]`; exports[`sql.binary 1`] = `"hello"`; @@ -8,4 +8,4 @@ exports[`sql.interval 1`] = `946818000000`; exports[`sql.interval 2`] = `86400`; -exports[`sql.type 1`] = `undefined`; +exports[`sql.type 1`] = `[SchemaValidationError: Query returned rows that do not conform with the schema.]`; diff --git a/packages/client/test/api-usage.test.ts b/packages/client/test/api-usage.test.ts index b8d774cc..40afcb58 100644 --- a/packages/client/test/api-usage.test.ts +++ b/packages/client/test/api-usage.test.ts @@ -1,8 +1,14 @@ /* eslint-disable @typescript-eslint/no-shadow */ import {beforeAll, beforeEach, expect, expectTypeOf, test, vi} from 'vitest' import {z} from 'zod' -import {fromError} from 'zod-validation-error' +import {fromError, isZodErrorLike} from 'zod-validation-error' import {createClient, createSqlTag, QueryError, sql} from '../src' +import {printError} from './snapshots' + +expect.addSnapshotSerializer({ + test: Boolean, + print: printError, +}) export let client: Awaited> @@ -152,7 +158,7 @@ test('sql.interval', async () => { select '2000-01-01T12:00:00Z'::timestamptz + ${sql.interval({days: 1, hours: 1})} as ts `) expect(result).toBeInstanceOf(Date) - expect(result).toMatchInlineSnapshot(`2000-01-02T13:00:00.000Z`) + expect(result).toMatchInlineSnapshot(`"2000-01-02T13:00:00.000Z"`) const interval = await client.oneFirst(sql`select ${sql.interval({days: 1})}`) expect(interval).toMatchInlineSnapshot(`"1 day"`) @@ -165,7 +171,7 @@ test('sql.binary', async () => { const result = await client.oneFirst(sql` select ${sql.binary(Buffer.from('hello'))} as b `) - expect(result).toMatchInlineSnapshot(`"\\x68656c6c6f"`) + expect(result).toMatchInlineSnapshot(`"\\\\x68656c6c6f"`) }) test('sql.json', async () => { @@ -270,39 +276,30 @@ test('sql.type', async () => { const error = await client.any(sql.type(StringId)`select id from usage_test`).catch(e => e) - expect(error.cause).toMatchInlineSnapshot(` + expect(error).toMatchInlineSnapshot(` + [QueryError]: [select-usage_test_8729cac]: Parsing rows failed { - "error": [ZodError: [ - { - "code": "invalid_type", - "expected": "string", - "received": "number", - "path": [ - "id" - ], - "message": "Expected string, received number" - } - ]], - "message": "[ - { - "code": "invalid_type", - "expected": "string", - "received": "number", - "path": [ - "id" - ], - "message": "Expected string, received number" - } - ]", - "name": "QueryErrorCause", + "message": "[select-usage_test_8729cac]: Parsing rows failed", "query": { "name": "select-usage_test_8729cac", - "parse": [Function], "sql": "select id from usage_test", - "templateArgs": [Function], "token": "sql", - "values": [], + "values": [] }, + "cause": { + "name": "ZodError", + "issues": [ + { + "code": "invalid_type", + "expected": "string", + "received": "number", + "path": [ + "id" + ], + "message": "Expected string, received number" + } + ] + } } `) }) @@ -313,18 +310,22 @@ test('sql.type', async () => { test('sql.type with custom error message', async () => { client = createClient(client.connectionString(), { ...client.options, - wrapQueryFn: query => { + pgpOptions: { + ...client.options.pgpOptions, + connect: { + ...client.options.pgpOptions?.connect, + application_name: 'impatient', + }, + }, + wrapQueryFn: queryFn => { const parentWrapper = client.options.wrapQueryFn || (x => x) return async (...args) => { - const parentQueryFn = parentWrapper(query) + const parentQueryFn = parentWrapper(queryFn) try { return await parentQueryFn(...args) } catch (e) { - if (e instanceof QueryError && e.message.endsWith('Parsing rows failed')) { - throw new QueryError(e.message, { - query: e.cause.query, - error: fromError(e.cause.error), - }) + if (e instanceof QueryError && isZodErrorLike(e.cause)) { + e.cause = fromError(e.cause) } throw e } @@ -335,19 +336,45 @@ test('sql.type with custom error message', async () => { const error = await client.any(sql.type(StringId)`select id from usage_test`).catch(e => e) - expect(error.cause).toMatchInlineSnapshot(` + expect(error).toMatchInlineSnapshot(` + [QueryError]: [select-usage_test_8729cac]: Parsing rows failed { - "error": [ZodValidationError: Validation error: Expected string, received number at "id"], - "message": "Validation error: Expected string, received number at "id"", - "name": "QueryErrorCause", + "message": "[select-usage_test_8729cac]: Parsing rows failed", "query": { "name": "select-usage_test_8729cac", - "parse": [Function], "sql": "select id from usage_test", - "templateArgs": [Function], "token": "sql", - "values": [], + "values": [] }, + "cause": { + "name": "ZodValidationError", + "message": "Validation error: Expected string, received number at \\"id\\"", + "cause": { + "name": "ZodError", + "issues": [ + { + "code": "invalid_type", + "expected": "string", + "received": "number", + "path": [ + "id" + ], + "message": "Expected string, received number" + } + ] + }, + "details": [ + { + "code": "invalid_type", + "expected": "string", + "received": "number", + "path": [ + "id" + ], + "message": "Expected string, received number" + } + ] + } } `) }) @@ -370,19 +397,45 @@ test('createSqlTag + sql.typeAlias', async () => { expect(result).toEqual({name: 'Bob'}) const err = await client.any(sql.typeAlias('Profile')`select 123 as name`).catch(e => e) - expect(err.cause).toMatchInlineSnapshot(` + expect(err).toMatchInlineSnapshot(` + [QueryError]: [select_245d49b]: Parsing rows failed { - "error": [ZodValidationError: Validation error: Expected string, received number at "name"], - "message": "Validation error: Expected string, received number at "name"", - "name": "QueryErrorCause", + "message": "[select_245d49b]: Parsing rows failed", "query": { "name": "select_245d49b", - "parse": [Function], "sql": "select 123 as name", - "templateArgs": [Function], "token": "sql", - "values": [], + "values": [] }, + "cause": { + "name": "ZodValidationError", + "message": "Validation error: Expected string, received number at \\"name\\"", + "cause": { + "name": "ZodError", + "issues": [ + { + "code": "invalid_type", + "expected": "string", + "received": "number", + "path": [ + "name" + ], + "message": "Expected string, received number" + } + ] + }, + "details": [ + { + "code": "invalid_type", + "expected": "string", + "received": "number", + "path": [ + "name" + ], + "message": "Expected string, received number" + } + ] + } } `) }) diff --git a/packages/client/test/errors.test.ts b/packages/client/test/errors.test.ts index 0197e687..7ab2215e 100644 --- a/packages/client/test/errors.test.ts +++ b/packages/client/test/errors.test.ts @@ -1,17 +1,15 @@ -import * as lodash from 'lodash' import * as path from 'path' import {beforeAll, expect, test} from 'vitest' -import {QueryError, createPool, sql} from '../src' -import {printPostgresErrorSnapshot} from './snapshots' +import {createPool, sql} from '../src' +import {printError} from './snapshots' + +const repoRoot = path.resolve(process.cwd(), '../..') expect.addSnapshotSerializer({ - test: (val: any) => val instanceof Error, - print: (val: any) => - printPostgresErrorSnapshot(lodash.pick(val, ['message', 'pg_code', 'pg_code_name', 'cause', 'code'])), + test: Boolean, + print: printError, }) -const repoRoot = path.resolve(process.cwd(), '../..') - expect.addSnapshotSerializer({ test: (val: unknown) => typeof val === 'string' && val.includes(repoRoot), print: (val: any) => @@ -38,52 +36,48 @@ beforeAll(async () => { test('one error', async () => { await expect(pool.one(sql`select * from test_errors where id > 1`)).rejects.toMatchInlineSnapshot( ` - [[Query select-test_errors_36f5f64]: Expected one row] + [QueryError]: [select-test_errors_36f5f64]: Expected one row { - "message": "[Query select-test_errors_36f5f64]: Expected one row", - "cause": { - "query": { - "name": "select-test_errors_36f5f64", - "sql": "select * from test_errors where id > 1", - "token": "sql", - "values": [] - }, - "result": { - "rows": [ - { - "id": 2, - "name": "two" - }, - { - "id": 3, - "name": "three" - } - ], - "command": "SELECT", - "rowCount": 2, - "fields": [ - { - "name": "id", - "tableID": 123456789, - "columnID": 1, - "dataTypeID": 123456789, - "dataTypeSize": 4, - "dataTypeModifier": -1, - "format": "text" - }, - { - "name": "name", - "tableID": 123456789, - "columnID": 2, - "dataTypeID": 123456789, - "dataTypeSize": -1, - "dataTypeModifier": -1, - "format": "text" - } - ] - }, - "message": "", - "name": "QueryErrorCause" + "message": "[select-test_errors_36f5f64]: Expected one row", + "query": { + "name": "select-test_errors_36f5f64", + "sql": "select * from test_errors where id > 1", + "token": "sql", + "values": [] + }, + "result": { + "rows": [ + { + "id": 2, + "name": "two" + }, + { + "id": 3, + "name": "three" + } + ], + "command": "SELECT", + "rowCount": 2, + "fields": [ + { + "name": "id", + "tableID": "[tableID]", + "columnID": 1, + "dataTypeID": "[dataTypeID]", + "dataTypeSize": 4, + "dataTypeModifier": -1, + "format": "text" + }, + { + "name": "name", + "tableID": "[tableID]", + "columnID": 2, + "dataTypeID": "[dataTypeID]", + "dataTypeSize": -1, + "dataTypeModifier": -1, + "format": "text" + } + ] } } `, @@ -91,11 +85,11 @@ test('one error', async () => { }) test('maybeOne error', async () => { - await expect(pool.maybeOne(sql`select * from test_errors where id > 1`)).rejects.toMatchInlineSnapshot(` - [[Query select-test_errors_36f5f64]: Expected at most one row] - { - "message": "[Query select-test_errors_36f5f64]: Expected at most one row", - "cause": { + await expect(pool.maybeOne(sql`select * from test_errors where id > 1`)).rejects.toMatchInlineSnapshot( + ` + [QueryError]: [select-test_errors_36f5f64]: Expected at most one row + { + "message": "[select-test_errors_36f5f64]: Expected at most one row", "query": { "name": "select-test_errors_36f5f64", "sql": "select * from test_errors where id > 1", @@ -118,37 +112,35 @@ test('maybeOne error', async () => { "fields": [ { "name": "id", - "tableID": 123456789, + "tableID": "[tableID]", "columnID": 1, - "dataTypeID": 123456789, + "dataTypeID": "[dataTypeID]", "dataTypeSize": 4, "dataTypeModifier": -1, "format": "text" }, { "name": "name", - "tableID": 123456789, + "tableID": "[tableID]", "columnID": 2, - "dataTypeID": 123456789, + "dataTypeID": "[dataTypeID]", "dataTypeSize": -1, "dataTypeModifier": -1, "format": "text" } ] - }, - "message": "", - "name": "QueryErrorCause" + } } - } - `) + `, + ) }) test('many error', async () => { - await expect(pool.many(sql`select * from test_errors where id > 100`)).rejects.toMatchInlineSnapshot(` - [[Query select-test_errors_34cad85]: Expected at least one row] - { - "message": "[Query select-test_errors_34cad85]: Expected at least one row", - "cause": { + await expect(pool.many(sql`select * from test_errors where id > 100`)).rejects.toMatchInlineSnapshot( + ` + [QueryError]: [select-test_errors_34cad85]: Expected at least one row + { + "message": "[select-test_errors_34cad85]: Expected at least one row", "query": { "name": "select-test_errors_34cad85", "sql": "select * from test_errors where id > 100", @@ -162,69 +154,66 @@ test('many error', async () => { "fields": [ { "name": "id", - "tableID": 123456789, + "tableID": "[tableID]", "columnID": 1, - "dataTypeID": 123456789, + "dataTypeID": "[dataTypeID]", "dataTypeSize": 4, "dataTypeModifier": -1, "format": "text" }, { "name": "name", - "tableID": 123456789, + "tableID": "[tableID]", "columnID": 2, - "dataTypeID": 123456789, + "dataTypeID": "[dataTypeID]", "dataTypeSize": -1, "dataTypeModifier": -1, "format": "text" } ] - }, - "message": "", - "name": "QueryErrorCause" + } } - } - `) + `, + ) }) test('syntax error', async () => { - await expect(pool.query(sql`select * frooom test_errors`)).rejects.toMatchInlineSnapshot(` - [[Query select_fb83277]: syntax error at or near "frooom"] - { - "message": "[Query select_fb83277]: syntax error at or near \\"frooom\\"", - "cause": { + await expect(pool.query(sql`select * frooom test_errors`)).rejects.toMatchInlineSnapshot( + ` + [QueryError]: [select_fb83277]: Executing query failed (syntax_error) + { + "message": "[select_fb83277]: Executing query failed (syntax_error)", "query": { "name": "select_fb83277", "sql": "select * frooom test_errors", "token": "sql", "values": [] }, - "error": { - "length": 95, + "cause": { "name": "error", + "message": "syntax error at or near \\"frooom\\"", + "length": 95, "severity": "ERROR", "code": "42601", "position": "10", "file": "scan.l", - "line": "123456789", + "line": "[line]", "routine": "scanner_yyerror", "query": "select * frooom test_errors" - }, - "message": "syntax error at or near \\"frooom\\"", - "name": "QueryErrorCause" + } } - } - `) + `, + ) const err: Error = await pool.query(sql`select * frooom test_errors`).catch(e => e) expect(err.stack).toMatchInlineSnapshot(` - Error: [Query select_fb83277]: syntax error at or near "frooom" + Error: [select_fb83277]: Executing query failed (syntax_error) at Object.query (/packages/client/src/client.ts::) at /packages/client/test/errors.test.ts:: `) - expect((err as QueryError).cause?.error?.stack).toMatchInlineSnapshot(` + expect((err as any).cause?.stack).toMatchInlineSnapshot(` error: syntax error at or near "frooom" at Parser.parseErrorMessage (/node_modules/.pnpm/pg-protocol@1.6.0/node_modules/pg-protocol/src/parser.ts::) at Parser.handlePacket (/node_modules/.pnpm/pg-protocol@1.6.0/node_modules/pg-protocol/src/parser.ts::) diff --git a/packages/client/test/pg-promise-usage.test.ts b/packages/client/test/pg-promise-usage.test.ts index 541f0657..815c0d3b 100644 --- a/packages/client/test/pg-promise-usage.test.ts +++ b/packages/client/test/pg-promise-usage.test.ts @@ -3,11 +3,21 @@ import {createClient, sql} from '../src' test("type parsers don't override each other", async () => { const client1 = createClient('postgresql://postgres:postgres@localhost:5432/postgres', { + pgpOptions: { + connect: { + application_name: 'impatient', + }, + }, applyTypeParsers: types => { types.setTypeParser(types.builtins.INT8, Number) }, }) const client2 = createClient('postgresql://postgres:postgres@localhost:5432/postgres', { + pgpOptions: { + connect: { + application_name: 'patient', + }, + }, applyTypeParsers: types => { types.setTypeParser(types.builtins.INT8, BigInt) }, diff --git a/packages/client/test/recipes.test.ts b/packages/client/test/recipes.test.ts index 0dc1bd27..2b78d350 100644 --- a/packages/client/test/recipes.test.ts +++ b/packages/client/test/recipes.test.ts @@ -3,14 +3,14 @@ import * as pgMem from 'pg-mem' import * as pgSqlAstParser from 'pgsql-ast-parser' import {beforeAll, beforeEach, expect, test, vi} from 'vitest' import {FieldInfo, createClient, sql} from '../src' -import {printPostgresErrorSnapshot} from './snapshots' +import {printError} from './snapshots' export let client: Awaited> let sqlProduced = [] as {sql: string; values: any[]}[] expect.addSnapshotSerializer({ - test: () => true, - print: val => printPostgresErrorSnapshot(val), + test: Boolean, + print: printError, }) beforeAll(async () => { @@ -74,6 +74,11 @@ test('Query logging', async () => { // Simplistic way of logging query times. For more accurate results, use process.hrtime() const log = vi.fn() const client = createClient('postgresql://postgres:postgres@localhost:5432/postgres', { + pgpOptions: { + connect: { + application_name: 'query-logger', + }, + }, wrapQueryFn: queryFn => async query => { const start = Date.now() const result = await queryFn(query) @@ -128,18 +133,18 @@ test('Query logging', async () => { "fields": [ { "name": "id", - "tableID": 123456789, + "tableID": "[tableID]", "columnID": 1, - "dataTypeID": 123456789, + "dataTypeID": "[dataTypeID]", "dataTypeSize": 4, "dataTypeModifier": -1, "format": "text" }, { "name": "name", - "tableID": 123456789, + "tableID": "[tableID]", "columnID": 2, - "dataTypeID": 123456789, + "dataTypeID": "[dataTypeID]", "dataTypeSize": -1, "dataTypeModifier": -1, "format": "text" @@ -171,22 +176,21 @@ test('query timeouts', async () => { const sleepSeconds = (shortTimeoutMs * 2) / 1000 await expect(impatient.one(sql`select pg_sleep(${sleepSeconds})`)).rejects.toThrowErrorMatchingInlineSnapshot( ` - [[Query select_9dcc021]: Query read timeout] + [QueryError]: [select_9dcc021]: Executing query failed { + "message": "[select_9dcc021]: Executing query failed", + "query": { + "name": "select_9dcc021", + "sql": "select pg_sleep($1)", + "token": "sql", + "values": [ + 0.04 + ] + }, "cause": { - "query": { - "name": "select_9dcc021", - "sql": "select pg_sleep($1)", - "token": "sql", - "values": [ - 0.04 - ] - }, - "error": { - "query": "select pg_sleep(0.04)" - }, + "name": "Error", "message": "Query read timeout", - "name": "QueryErrorCause" + "query": "select pg_sleep(0.04)" } } `, @@ -203,6 +207,7 @@ test('switchable clients', async () => { pgpOptions: { connect: { query_timeout: shortTimeoutMs, + application_name: 'impatient', }, }, }) @@ -210,11 +215,17 @@ test('switchable clients', async () => { pgpOptions: { connect: { query_timeout: shortTimeoutMs * 3, + application_name: 'patient', }, }, }) const appClient = createClient(client.connectionString(), { + pgpOptions: { + connect: { + application_name: 'app', + }, + }, wrapQueryFn: _queryFn => { return async query => { let clientToUse = patientClient @@ -242,22 +253,21 @@ test('switchable clients', async () => { select pg_sleep(${sleepSeconds}) `), ).rejects.toThrowErrorMatchingInlineSnapshot(` - [[Query select_6289211]: Query read timeout] + [QueryError]: [select_6289211]: Executing query failed { + "message": "[select_6289211]: Executing query failed", + "query": { + "name": "select_6289211", + "sql": "\\n select pg_sleep($1)\\n ", + "token": "sql", + "values": [ + 0.04 + ] + }, "cause": { - "query": { - "name": "select_6289211", - "sql": "\\n select pg_sleep($1)\\n ", - "token": "sql", - "values": [ - 0.04 - ] - }, - "error": { - "query": "\\n select pg_sleep(0.04)\\n " - }, + "name": "Error", "message": "Query read timeout", - "name": "QueryErrorCause" + "query": "\\n select pg_sleep(0.04)\\n " } } `) diff --git a/packages/client/test/slonik37.test.ts b/packages/client/test/slonik37.test.ts index 4154613c..4dd774e8 100644 --- a/packages/client/test/slonik37.test.ts +++ b/packages/client/test/slonik37.test.ts @@ -313,7 +313,7 @@ test('sql.type', async () => { const error = await client.any(sql.type(StringId)`select id from test_slonik37`).catch(e => e) - expect(error.cause).toMatchSnapshot() + expect(error).toMatchSnapshot() }) /** @@ -334,7 +334,7 @@ test('createSqlTag + sql.typeAlias', async () => { expect(result).toEqual({name: 'Bob'}) const err = await client.any(sql.typeAlias('Profile')`select 123 as name`).catch(e => e) - expect(err.cause).toMatchSnapshot() + expect(err).toMatchSnapshot() }) // codegen:end diff --git a/packages/client/test/snapshots.ts b/packages/client/test/snapshots.ts index b44375bb..929c1ac3 100644 --- a/packages/client/test/snapshots.ts +++ b/packages/client/test/snapshots.ts @@ -1,19 +1,26 @@ -export function printPostgresErrorSnapshot(val: any): string { - return ( - `[${val.message}]\n`.replace('[undefined]\n', '') + - JSON.stringify( - val, - function (key, value) { - if (key === 'dataTypeID' || key === 'tableID') { - return 123_456_789 // avoid unstable pg generated ids - } - if (this.name === 'error' && key === 'line') { - return '123456789' // avoid unstable line numbers of generated statements - } +import {isValidationErrorLike} from 'zod-validation-error' - return value - }, - 2, - ) +export function printError(val: any): string { + const message = val instanceof Error ? `[${val.constructor.name}]: ${val.message}` : undefined + if (message && isValidationErrorLike(val)) { + return message + } + const props = JSON.stringify( + val, + function reviver(this: any, key, value: any) { + if (value?.constructor?.name?.endsWith('Error')) { + const keys = ['name', ...Object.getOwnPropertyNames(value).filter(p => p !== 'stack')] + return Object.fromEntries(keys.map(k => [k, value[k]])) + } + if (key === 'dataTypeID' || key === 'tableID') { + return `[${key}]` // avoid unstable pg generated ids + } + if (this.name === 'error' && key === 'line') { + return `[${key}]` // avoid unstable line numbers of generated statements + } + return value + }, + 2, ) + return [message, props].filter(Boolean).join('\n') } diff --git a/packages/client/test/zod.test.ts b/packages/client/test/zod.test.ts index f5ac9cd2..c883d7e2 100644 --- a/packages/client/test/zod.test.ts +++ b/packages/client/test/zod.test.ts @@ -89,36 +89,33 @@ test('Refine schemas', async () => { await expect(getResult()).rejects.toMatchInlineSnapshot(` { + "message": "[select-zod_test_83bbed1]: Parsing rows failed", + "query": { + "name": "select-zod_test_83bbed1", + "sql": "\\n select * from zod_test\\n ", + "token": "sql", + "values": [] + }, "cause": { - "query": { - "name": "select-zod_test_83bbed1", - "sql": "\\n select * from zod_test\\n ", - "token": "sql", - "values": [] - }, - "error": { - "issues": [ - { - "code": "invalid_type", - "expected": "string", - "received": "undefined", - "path": [ - "name" - ], - "message": "Required" - }, - { - "code": "custom", - "message": "id must be even", - "path": [ - "id" - ] - } - ], - "name": "ZodError" - }, - "message": "[\\n {\\n \\"code\\": \\"invalid_type\\",\\n \\"expected\\": \\"string\\",\\n \\"received\\": \\"undefined\\",\\n \\"path\\": [\\n \\"name\\"\\n ],\\n \\"message\\": \\"Required\\"\\n },\\n {\\n \\"code\\": \\"custom\\",\\n \\"message\\": \\"id must be even\\",\\n \\"path\\": [\\n \\"id\\"\\n ]\\n }\\n]", - "name": "QueryErrorCause" + "issues": [ + { + "code": "invalid_type", + "expected": "string", + "received": "undefined", + "path": [ + "name" + ], + "message": "Required" + }, + { + "code": "custom", + "message": "id must be even", + "path": [ + "id" + ] + } + ], + "name": "ZodError" } } `) diff --git a/packages/migra/package.json b/packages/migra/package.json index 4c203951..5db7b0bb 100644 --- a/packages/migra/package.json +++ b/packages/migra/package.json @@ -25,7 +25,7 @@ "@pgkit/client": "workspace:^", "@pgkit/schemainspect": "workspace:^", "@trpc/server": "^10.45.2", - "trpc-cli": "^0.2.1", + "trpc-cli": "^0.5.0", "zod": "^3.23.8" }, "devDependencies": { diff --git a/packages/migra/src/changes.ts b/packages/migra/src/changes.ts index 64bd771a..a42892e4 100644 --- a/packages/migra/src/changes.ts +++ b/packages/migra/src/changes.ts @@ -33,6 +33,7 @@ const THINGS = new Set([ 'collations', 'rlspolicies', 'triggers', + 'domains', ]) const PK = 'PRIMARY KEY' @@ -822,13 +823,9 @@ export class Changes { // TypeScript does not support dynamic property access in the same way as Python. // You would need to implement a method that takes the name as a parameter and returns the appropriate function. getChangesFor =

(name: P) => { - if (THINGS.has(name)) { - return (params?: Parameters[2]) => { - return statements_for_changes(this.i_from[name], this.i_target[name], params) - } - } + if (!THINGS.has(name)) throw new Error(`AttributeError: ${name}. Changes can be for: ${[...THINGS].join(',')}`) - throw new Error(`AttributeError: ${name}`) + return (opts?: StatementsForChangesParams) => statements_for_changes(this.i_from[name], this.i_target[name], opts) } schemas = this.getChangesFor('schemas') @@ -836,6 +833,7 @@ export class Changes { enums = this.getChangesFor('enums') rlspolicies = this.getChangesFor('rlspolicies') privileges = this.getChangesFor('privileges') + domains = this.getChangesFor('domains') } export type GetSelectableChangesOptions = Parameters[1] diff --git a/packages/migra/src/command.ts b/packages/migra/src/command.ts index e4f3f07e..d413dcda 100644 --- a/packages/migra/src/command.ts +++ b/packages/migra/src/command.ts @@ -31,10 +31,7 @@ const argContext = async (x: Queryable | string): Promise - -export const run = async (dburlFrom: Queryable | string, dburlTarget: Queryable | string, args: Flags = {}) => { +export const run = async (dburlFrom: Queryable | string, dburlTarget: Queryable | string, args: MigraOptions = {}) => { // const {schema} = args // const {excludeSchema} = args // const out: typeof process.stdout = args.out || process.stdout @@ -100,6 +97,30 @@ export const run = async (dburlFrom: Queryable | string, dburlTarget: Queryable } const t = initTRPC.meta().create() +export const MigraOptions = z.object({ + unsafe: z.boolean().default(false).describe('Prevent migra from erroring upon generation of drop statements.'), + schema: z.string().optional().describe('Restrict output to statements for a particular schema'), + excludeSchema: z + .string() + .optional() + .describe('Restrict output to statements for all schemas except the specified schema'), + createExtensionsOnly: z + .boolean() + .default(false) + .describe('Only output "create extension..." statements, nothing else.'), + ignoreExtensionVersions: z.boolean().default(false).describe('Ignore the versions when comparing extensions.'), + withPrivileges: z + .boolean() + .default(false) + .describe('Also output privilege differences (ie. grant/revoke statements)'), + forceUtf8: z.boolean().default(false).describe('Force UTF-8 encoding for output'), +}) +export type MigraOptions = z.infer + +// export type Flags = MigraOptions + +const MigraRunInput = z.tuple([z.string().describe('dburlFrom'), z.string().describe('dburlTarget'), MigraOptions]) + const router = t.router({ run: t.procedure .meta({ @@ -109,38 +130,7 @@ const router = t.router({ `migra 'postgresql://postgres:postgres@localhost:5432/migra_test_collations_a' 'postgresql://postgres:postgres@localhost:5432/migra_test_collations_a' --unsafe`, ], }) - .input( - z.tuple([ - z.string().describe('dburlFrom'), - z.string().describe('dburlTarget'), - z - .object({ - unsafe: z - .boolean() - .default(false) - .describe('Prevent migra from erroring upon generation of drop statements.'), - schema: z.string().optional().describe('Restrict output to statements for a particular schema'), - excludeSchema: z - .string() - .optional() - .describe('Restrict output to statements for all schemas except the specified schema'), - createExtensionsOnly: z - .boolean() - .default(false) - .describe('Only output "create extension..." statements, nothing else.'), - ignoreExtensionVersions: z - .boolean() - .default(false) - .describe('Ignore the versions when comparing extensions.'), - withPrivileges: z - .boolean() - .default(false) - .describe('Also output privilege differences (ie. grant/revoke statements)'), - forceUtf8: z.boolean().default(false).describe('Force UTF-8 encoding for output'), - }) - .optional(), - ]), - ) + .input(MigraRunInput) .query(async ({input: [dburlFrom, dburlTarget, args]}) => { const {sql} = await run(dburlFrom, dburlTarget, args) return sql diff --git a/packages/migra/src/index.ts b/packages/migra/src/index.ts index aa97e910..d138eb30 100644 --- a/packages/migra/src/index.ts +++ b/packages/migra/src/index.ts @@ -1,5 +1,5 @@ export {Changes} from './changes' -export {run, type Flags} from './command' +export {run, type MigraOptions} from './command' export {Migration} from './migra' export {Statements, UnsafeMigrationException} from './statements' diff --git a/packages/migra/src/migra.ts b/packages/migra/src/migra.ts index c672ae0d..0b19e93c 100644 --- a/packages/migra/src/migra.ts +++ b/packages/migra/src/migra.ts @@ -107,6 +107,9 @@ export class Migration { this.add(this.changes.sequences({drops_only: true})) this.add(this.changes.enums({drops_only: true, modifications: false})) this.add(this.changes.extensions({drops_only: true, modifications: false})) + this.add(this.changes.domains({drops_only: true})) + + this.add(this.changes.domains({creations_only: true})) this.add(this.changes.non_mv_indexes({creations_only: true})) this.add(this.changes.pk_constraints({creations_only: true})) this.add(this.changes.non_pk_constraints({creations_only: true})) diff --git a/packages/migra/test/fixtures.ts b/packages/migra/test/fixtures.ts index ae1d7a47..94bcd98c 100644 --- a/packages/migra/test/fixtures.ts +++ b/packages/migra/test/fixtures.ts @@ -3,7 +3,7 @@ import * as fs from 'fs' import {kebabCase} from 'lodash' import * as path from 'path' import * as sqlFormatter from 'sql-formatter' -import {type Flags} from '../src/command' +import {type MigraOptions} from '../src/command' export const format = (query: string) => { try { @@ -16,14 +16,14 @@ export const format = (query: string) => { const fixturesDir = path.join(__dirname, 'FIXTURES') const fixtureNames = fs.readdirSync(fixturesDir) -const argsMap: Record = { +const argsMap: Record = { singleschema: {schema: 'goodschema'}, excludeschema: {excludeSchema: 'excludedschema'}, singleschema_ext: {createExtensionsOnly: true}, extversions: {ignoreExtensionVersions: false}, } -export const setup = async (url: string, admin: Client, prefix: string) => { +export const createDB = async (url: string, admin: Client, prefix: string) => { const db = url.split('/').at(-1) const variant = db.split('_').at(-1) const name = db.replace(prefix + '_', '').slice(0, -1 - variant.length) @@ -38,11 +38,16 @@ export const setup = async (url: string, admin: Client, prefix: string) => { await admin.query(sql`create database ${sql.identifier([db])}`) const pool = createClient(connectionString) + return {connectionString, pool, name, variant} +} + +export const setup = async (url: string, admin: Client, prefix: string) => { + const {pool, name, variant} = await createDB(url, admin, prefix) const filepath = path.join(fixturesDir, name, `${variant}.sql`) const query = fs.readFileSync(filepath, 'utf8') await pool.query(sql`create role schemainspect_test_role`).catch(e => { - const isRoleExists = (e as Error)?.message?.includes(`role "schemainspect_test_role" already exists`) + const isRoleExists = (e as Error)?.message?.endsWith(`(duplicate_object)`) if (!isRoleExists) throw e as Error }) @@ -54,8 +59,8 @@ export const setup = async (url: string, admin: Client, prefix: string) => { export const getFixtures = (prefix: string) => fixtureNames.map(name => { const variant = (ab: 'a' | 'b', admin: Client) => - admin.pgp.$cn.toString().replace(/postgres$/, `${prefix}_${name}_${ab}`) - const args: Flags = { + admin.connectionString().replace(/postgres$/, `${prefix}_${name}_${ab}`) + const args: MigraOptions = { unsafe: true, ignoreExtensionVersions: true, ...(name in argsMap && argsMap[name]), @@ -79,7 +84,7 @@ export const getFixtures = (prefix: string) => .replace(/exclude-schema/, 'exclude_schema') if (v === false) return [] if (v === true) return [arg] - return [arg, v as string] + return [arg, v] }) }, } as const diff --git a/packages/migra/test/limitations.test.ts b/packages/migra/test/limitations.test.ts new file mode 100644 index 00000000..80b8d447 --- /dev/null +++ b/packages/migra/test/limitations.test.ts @@ -0,0 +1,29 @@ +import {createPool, sql} from '@pgkit/client' +import {beforeAll, describe, expect, test} from 'vitest' +import * as migra from '../src' +import {setup, format, createDB} from './fixtures' + +export let admin: Awaited> + +beforeAll(async () => { + admin = createPool('postgresql://postgres:postgres@localhost:5432/postgres') +}) + +describe('prior limitations', () => { + test('domains', async () => { + const a = await createDB(admin.connectionString().replace(/postgres$/, 'a'), admin, 'domain_test') + const b = await createDB(admin.connectionString().replace(/postgres$/, 'b'), admin, 'domain_test') + + await a.pool.query(sql`create domain test_domain as text`) + await b.pool.query(sql`create domain test_domain as integer`) + + const result = await migra.run(a.pool, b.pool, {unsafe: true}) + expect(result.sql.trim()).toMatchInlineSnapshot(` + "drop domain "public"."test_domain"; + + create domain "public"."test_domain" + as integer + null" + `) + }) +}) diff --git a/packages/migra/test/python-parity.test.ts b/packages/migra/test/python-parity.test.ts index 5dd09004..456442d8 100644 --- a/packages/migra/test/python-parity.test.ts +++ b/packages/migra/test/python-parity.test.ts @@ -23,7 +23,7 @@ test.each(fixtures)( cwd: process.cwd(), env: process.env, reject: false, // migra exits with a non-zero code when there are changes - }).then(p => format(p.stdout)) + }).then(p => format(p.stderr).trim() || format(p.stdout)) const migra = await runMigra(a, b, args()) const actual = format(migra.sql) diff --git a/packages/migrator/package.json b/packages/migrator/package.json index e6172a9f..86ff5717 100644 --- a/packages/migrator/package.json +++ b/packages/migrator/package.json @@ -16,7 +16,7 @@ "dist" ], "scripts": { - "dev": "PGKIT_MIGRATIONS_PATH=/Users/mmkal/src/pgkit/packages/admin/zignoreme/migrator/migrations PGKIT_MIGRATIONS_TABLE_NAME=admin_test_migrations tsx src/bin", + "dogfood": "PGKIT_MIGRATIONS_PATH=/Users/mmkal/src/pgkit/packages/admin/zignoreme/migrator/migrations PGKIT_MIGRATIONS_TABLE_NAME=admin_test_migrations tsx src/bin", "prepack": "pnpm build", "build": "tsc -p tsconfig.lib.json", "test": "vitest run" @@ -49,7 +49,7 @@ "picocolors": "^1.0.1", "strip-ansi": "6.0.0", "tasuku": "^2.0.1", - "trpc-cli": "^0.3.0", + "trpc-cli": "^0.5.0", "umzug": "^3.7.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.23.0", diff --git a/packages/migrator/src/cli.ts b/packages/migrator/src/cli.ts index ecfdb057..cc33ddb0 100644 --- a/packages/migrator/src/cli.ts +++ b/packages/migrator/src/cli.ts @@ -1,7 +1,10 @@ import * as prompt from '@inquirer/prompts' +import * as childProcess from 'child_process' +import * as fs from 'fs' +import * as os from 'os' import * as path from 'path' import * as colors from 'picocolors' -import {trpcCli} from 'trpc-cli' +import {createCli} from 'trpc-cli' import {Migrator} from './migrator' import {createMigratorRouter} from './router' @@ -14,7 +17,7 @@ export const getMigratorFromEnv = () => { } export const createMigratorCli = (migrator: Migrator) => { - return trpcCli({ + return createCli({ router: createMigratorRouter(), context: {migrator, confirm}, }) @@ -22,9 +25,53 @@ export const createMigratorCli = (migrator: Migrator) => { export type MigratorCLI = ReturnType -export const confirm = async (input: string) => { - if (!input.trim()) return false +export const confirm = async (input: string, options?: {readonly?: boolean}): Promise => { + let currentInput = input - const message = `${colors.underline('Please confirm you want to run the following')}:\n\n${input}` - return prompt.confirm({message}) + while (currentInput.trim()) { + const message = `${colors.underline('Please confirm you want to run the following')}:\n\n${currentInput}` + const choice = await prompt.select({ + message, + choices: [ + {name: 'Yes', value: 'yes'}, + {name: 'No', value: 'no'}, + { + name: 'Edit first', + disabled: options?.readonly, + description: + "Your default editor will be used. You'll be asked again if you want to run the edit migration afterwards.", + value: 'edit', + }, + ], + }) + + if (choice === 'yes') return currentInput + if (choice === 'no') return null + + currentInput = await editInDefaultEditor(currentInput) + } + + return null +} + +async function editInDefaultEditor(input: string): Promise { + const tempFile = path.join(os.tmpdir(), `pgkit-migrator-edit-${Date.now()}.sql`) + fs.writeFileSync(tempFile, input.trim() + '\n') + + const editor = process.env.EDITOR || 'vi' + + try { + await new Promise((resolve, reject) => { + const child = childProcess.spawn(editor, [tempFile], {stdio: 'inherit'}) + child.on('exit', code => { + if (code === 0) resolve() + else reject(new Error(`Editor exited with code ${code}`)) + }) + }) + + const editedContent = fs.readFileSync(tempFile, 'utf8') + return editedContent.trim() + } finally { + fs.unlinkSync(tempFile) + } } diff --git a/packages/migrator/src/migrator.ts b/packages/migrator/src/migrator.ts index a3e8fbc5..09fb585c 100644 --- a/packages/migrator/src/migrator.ts +++ b/packages/migrator/src/migrator.ts @@ -6,7 +6,7 @@ import {createHash, randomInt} from 'crypto' import {existsSync, readFileSync} from 'fs' import * as fs from 'fs/promises' import * as path from 'path' -import {trpcCli} from 'trpc-cli' +import {createCli} from 'trpc-cli' import {confirm} from './cli' import {createMigratorRouter} from './router' import * as templates from './templates' @@ -43,6 +43,14 @@ export class Migrator { protected initialConfig: MigratorConfig constructor(params: MigratorConstructorParams) { + // todo: sensible defaults somewhere? Either way, these should always be defined + if (!params.migrationsPath) { + throw new Error('migrationsPath is required') + } + if (!params.migrationTableName) { + throw new Error('migrationTableName is required') + } + this.initialConfig = { task: async (name, fn) => { this.logger.info('Starting', name) @@ -87,7 +95,7 @@ export class Migrator { cli() { const router = createMigratorRouter() - return trpcCli({router, context: {migrator: this, confirm}}) + return createCli({router, context: {migrator: this, confirm}}) } /** Gets a hexadecimal integer to pass to postgres's `select pg_advisory_lock()` function */ @@ -263,7 +271,7 @@ export class Migrator { async unlock(params: {confirm: Confirm}) { const message = '*** WARNING ***: This will release the advisory lock. If you have multiple servers running migrations, this could cause more than one to try to apply migrations simultaneously. Are you sure?' - if (await params.confirm(message)) { + if (await params.confirm(message, {readonly: true})) { await this.releaseAdvisoryLock() } } @@ -295,13 +303,14 @@ export class Migrator { */ async goto(params: {name: string; confirm: Confirm; purgeDisk?: boolean}) { const diffTo = await this.getDiffTo({name: params.name}) - if (await params.confirm(diffTo)) { + const confirmation = await params.confirm(diffTo) + if (confirmation) { await this.useAdvisoryLock(async () => { - await this.client.query(sql.raw(diffTo)) + await this.client.query(sql.raw(confirmation)) await this.baseline({ to: params.name, purgeDisk: params.purgeDisk, - confirm: async () => true, + confirm: async () => 'confirmed', }) }) } @@ -364,7 +373,7 @@ export class Migrator { `, ] - const ok = await params.confirm(this.renderConfirmable(queries)) + const ok = await params.confirm(this.renderConfirmable(queries), {readonly: true}) if (!ok) return await this.useContext(async context => { @@ -509,11 +518,12 @@ export class Migrator { '', `Note: this will not update the database other than the migrations table. It will modify your filesystem.`, ] - if (await params.confirm(lines.join('\n'))) { - await this.baseline({to: params.from, purgeDisk: true, confirm: async () => true}) + const confirmation = await params.confirm(lines.join('\n'), {readonly: true}) + if (confirmation) { + await this.baseline({to: params.from, purgeDisk: true, confirm: async () => 'confirmed'}) if (diff) { const created = await this.create({content: diff}) - await this.baseline({to: created.name, confirm: async () => true}) + await this.baseline({to: created.name, confirm: async () => 'confirmed'}) } } return this.list() @@ -533,8 +543,9 @@ export class Migrator { */ async updateDbToMatchDefinitions(params: {confirm: Confirm}) { const diff = await this.diffToDefinitions() - if (await params.confirm(diff)) { - await this.client.query(sql.raw(diff)) + const confirmation = await params.confirm(diff) + if (confirmation) { + await this.client.query(sql.raw(confirmation)) } } @@ -546,16 +557,16 @@ export class Migrator { const oldContent = await fs.readFile(this.definitionsFile, 'utf8').catch(() => '') const changed = formatSql(diff) !== formatSql(oldContent) - const doUpdate = changed && (await params.confirm(diff)) + const confirmation = changed ? await params.confirm(diff) : null - if (doUpdate) { + if (confirmation) { await fs.mkdir(path.dirname(this.definitionsFile), {recursive: true}) - await fs.writeFile(this.definitionsFile, diff) + await fs.writeFile(this.definitionsFile, confirmation) } return { path: this.definitionsFile, changed, - updated: doUpdate, + updated: confirmation, content: diff, } } @@ -601,7 +612,7 @@ export class Migrator { async repair(params: {confirm: Confirm}) { const diff = await this.getRepairDiff() - const confirmed = await params.confirm(this.renderConfirmable(diff)) + const confirmed = await params.confirm(this.renderConfirmable(diff), {readonly: true}) if (!confirmed) { return {drifted: diff.length > 0, updated: false} } @@ -681,9 +692,10 @@ export class Migrator { async wipe(params: {confirm: Confirm}) { const diff = await this.wipeDiff() - const warning = '### THIS WILL DELETE EVERYTHING IN YOUR DATABASE! ###' - if (await params.confirm(warning + '\n\n' + diff)) { - await this.client.query(sql.raw(diff)) + const warning = '/* ### THIS WILL DELETE EVERYTHING IN YOUR DATABASE! ### */' + const confirmation = await params.confirm(warning + '\n\n' + diff) + if (confirmation) { + await this.client.query(sql.raw(confirmation)) } } @@ -778,7 +790,11 @@ export class Migrator { * } */ async wrapMigra(...args: Parameters) { - const result = await migra.run(args[0], args[1], {unsafe: true, ...args[2]}) + const result = await migra.run(args[0], args[1], { + unsafe: true, + ...this.initialConfig.defaultMigraOptions, + ...args[2], + }) const formatted = formatSql(result.sql) return { result, diff --git a/packages/migrator/src/types.ts b/packages/migrator/src/types.ts index 9606f178..34b39cd8 100644 --- a/packages/migrator/src/types.ts +++ b/packages/migrator/src/types.ts @@ -1,4 +1,5 @@ import {sql, Client, Queryable} from '@pgkit/client' +import {type MigraOptions} from '@pgkit/migra' import * as umzug from 'umzug' export interface MigratorContext { @@ -6,9 +7,11 @@ export interface MigratorContext { sql: typeof sql } +export type Awaitable = T | Promise + export type Migration = (params: umzug.MigrationParams) => Promise -export type Confirm = (sql: string) => boolean | Promise +export type Confirm = (sql: string, options?: {readonly?: boolean}) => Awaitable export interface BaseListedMigration { name: string @@ -81,6 +84,9 @@ export interface MigratorConfig { */ task: Task logger: Logger + + /** flags passed to [migra](https://npmjs.com/package/@pgkit/migra) - which is called for various introspection tasks */ + defaultMigraOptions?: MigraOptions } export interface MigratorConstructorParams extends Omit { diff --git a/packages/pgkit/package.json b/packages/pgkit/package.json index 750d9e15..e8ef729f 100644 --- a/packages/pgkit/package.json +++ b/packages/pgkit/package.json @@ -7,16 +7,16 @@ ], "exports": { "./client": { - "default": "./dist/client.js", - "types": "./dist/client.d.ts" + "types": "./dist/client.d.ts", + "default": "./dist/client.js" }, "./migrator": { - "default": "./dist/migrator.js", - "types": "./dist/migrator.d.ts" + "types": "./dist/migrator.d.ts", + "default": "./dist/migrator.js" }, "./config": { - "default": "./dist/config.js", - "types": "./dist/config.d.ts" + "types": "./dist/config.d.ts", + "default": "./dist/config.js" } }, "bin": { @@ -48,6 +48,6 @@ "@pgkit/typegen": "workspace:^", "express": "^4.18.2", "importx": "^0.4.4", - "trpc-cli": "https://pkg.pr.new/mmkal/trpc-cli@38" + "trpc-cli": "^0.5.0" } } diff --git a/packages/pgkit/src/config.ts b/packages/pgkit/src/config.ts index 66c816ab..1dd5610e 100644 --- a/packages/pgkit/src/config.ts +++ b/packages/pgkit/src/config.ts @@ -1,3 +1,5 @@ +import {Client} from '@pgkit/client' +import {type MigratorConstructorParams} from '@pgkit/migrator/dist/types' import {type Options as TypegenOptions} from '@pgkit/typegen' import * as fs from 'fs' import * as path from 'path' @@ -6,20 +8,22 @@ export type Config = { client: { connectionString: string } - typegen?: Partial - migrator?: { - connectionString?: string - /** @default '${cwd}/migrations' */ - migrationTableName?: string - /** @default 'migrations' */ - migrationsPath?: string - } + typegen?: + | Partial + | ((params: {client: Client; defaults: typeof import('@pgkit/typegen').defaults}) => Partial) + migrator?: + | Omit + | ((params: {client: Client}) => Omit) +} + +export type ResolvedConfig = { + [K in keyof Config]: Exclude unknown> } export const defineConfig = (config: Config) => config export const loadConfig = async (): Promise => { - const importx = await import('importx') + const importx = await import('importx') // todo: consider c12 instead const configLocations = ['pgkit.config.ts', 'pgkit.config.js'] let config: Config | undefined let cwd = process.cwd() @@ -30,7 +34,7 @@ export const loadConfig = async (): Promise => { config = await importx.import(filepath, { parentURL: new URL(`file://${cwd}`), }) - if ((config as {default?: Config}).default) { + while ((config as {default?: Config}).default) { config = (config as {default?: Config}).default } if (!config) { diff --git a/packages/pgkit/src/router.ts b/packages/pgkit/src/router.ts index 3d5de04f..32c72737 100644 --- a/packages/pgkit/src/router.ts +++ b/packages/pgkit/src/router.ts @@ -1,7 +1,7 @@ import {Client, createClient} from '@pgkit/client' import {Migrator, createMigratorRouter} from '@pgkit/migrator' import {confirm} from '@pgkit/migrator/dist/cli' -import {generate} from '@pgkit/typegen' +import {defaults, generate} from '@pgkit/typegen' import express from 'express' import * as trpcCli from 'trpc-cli' import {z} from 'trpc-cli' @@ -12,11 +12,8 @@ const t = trpcCli.trpcServer.initTRPC.meta().create() const procedureWithClient = t.procedure.use(async ({ctx, next}) => { const config = await loadConfig() const client = (clientSingleton.client ||= createClient(config.client.connectionString)) - const migrator = (clientSingleton.migrator ||= new Migrator({ - client, - migrationsPath: config.migrator?.migrationsPath, - migrationTableName: config.migrator?.migrationTableName, - })) + const migratorOptions = typeof config.migrator === 'function' ? config.migrator({client}) : config.migrator + const migrator = (clientSingleton.migrator ||= new Migrator({client, ...migratorOptions})) return next({ ctx: {...ctx, client, migrator, config}, }) @@ -55,12 +52,17 @@ export const router = t.router({ watch = watch ?? input.lazy if (input.lazy && !watch) throw new Error('Cannot specify --watch=false and --lazy') + const typegenOptions = + typeof ctx.config.typegen === 'function' + ? ctx.config.typegen({defaults, client: ctx.client}) + : ctx.config.typegen + const run = await generate({ connectionString: ctx.client, ...input, - ...(ctx.config.typegen && + ...(typegenOptions && Object.fromEntries( - Object.entries(ctx.config.typegen).filter(([key]) => !ctx.inputKeys.has(key)), // don't override options explicitly passed, do override defaults + Object.entries(typegenOptions).filter(([key]) => !ctx.inputKeys.has(key)), // don't override options explicitly passed, do override defaults )), }) diff --git a/packages/schemainspect/src/pg/obj.ts b/packages/schemainspect/src/pg/obj.ts index 3826de42..42dfd46e 100644 --- a/packages/schemainspect/src/pg/obj.ts +++ b/packages/schemainspect/src/pg/obj.ts @@ -2,9 +2,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-shadow */ -/* eslint-disable @typescript-eslint/brace-style */ /* eslint-disable unicorn/prefer-ternary */ -/* eslint-disable @typescript-eslint/padding-line-between-statements */ /* eslint-disable max-lines */ /* eslint-disable prefer-const */ /* eslint-disable guard-for-in */ @@ -1163,20 +1161,17 @@ export class PostgreSQL extends DBInspector { queryResults: Record = {} - private constructor(c: Queryable, options: PostgreSQLOptions = {}) { + private constructor(c: Queryable, serverVersion: string, options: PostgreSQLOptions = {}) { super(c, {include_internal: options.include_internal, i_remembered_to_call_initialize_super: true}) this.is_raw_psyco_connection = false this.is_raw_psyco_connection = true // we don't have sqlalchemy so need to avoid it - // let pg_version: number - // try { - // pg_version = c.dialect.server_version_info[0] - // } catch { - // pg_version = Number.parseInt(c.connection.server_version.toString().slice(0, -4), 10) - const pg_version = 12 + const pg_version = Number.parseInt(serverVersion.split('.')[0], 10) + if (Number.isNaN(pg_version)) { + throw new Error(`Unexpected server version: ${serverVersion}`) + } this.is_raw_psyco_connection = true - // } this.pg_version = pg_version @@ -1255,9 +1250,12 @@ export class PostgreSQL extends DBInspector { this.TRIGGERS_QUERY = processed(TRIGGERS_QUERY) } - static async create(connection: Queryable | string, props?: PostgreSQLOptions): Promise { - const c = typeof connection === 'string' ? createClient(connection) : connection - const instance = new PostgreSQL(c, props) + static async create(connectable: Queryable | string, props?: PostgreSQLOptions): Promise { + const connection = typeof connectable === 'string' ? createClient(connectable) : connectable + const serverVersion = await connection.oneFirst<{server_version: string}>( + sql`select current_setting('server_version') server_version`, + ) + const instance = new PostgreSQL(connection, serverVersion, props) await instance.load_all_async() return instance } @@ -2024,7 +2022,7 @@ export class PostgreSQL extends DBInspector { // deviation: this is new - idea being you can have use a serialized version served via API/S3/filesystem static fromJSON(json: any): PostgreSQL { - const instance = new PostgreSQL(null as never) + const instance = new PostgreSQL(null as never, '12.0.0') for (const prop of PostgreSQL.serializationKeys) { const Cls = PostgreSQL.serializationPropsClasses[prop] instance[prop] = Object.fromEntries( @@ -2038,7 +2036,7 @@ export class PostgreSQL extends DBInspector { // deviation: this is new - help making a baseline sql script for the whole database static empty(): PostgreSQL { - return new PostgreSQL(null as never) + return new PostgreSQL(null as never, '12.0.0') } equals(other: PostgreSQL): boolean { diff --git a/packages/schemainspect/test/__snapshots__/collations.json b/packages/schemainspect/test/__snapshots__/collations.json index 2df3847a..647f6cf9 100644 --- a/packages/schemainspect/test/__snapshots__/collations.json +++ b/packages/schemainspect/test/__snapshots__/collations.json @@ -23,7 +23,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "b": { @@ -39,7 +39,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "c": { @@ -55,7 +55,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -88,7 +88,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "b": { @@ -104,7 +104,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "c": { @@ -120,7 +120,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -151,7 +151,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "b": { @@ -167,7 +167,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "c": { @@ -183,7 +183,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -202,7 +202,7 @@ "plpgsql": { "name": "plpgsql", "schema": "pg_catalog", - "version": "1.0" + "version": "1.x" } }, "functions": {}, @@ -212,7 +212,7 @@ "name": "numeric", "schema": "public", "provider": "icu", - "lc_collate": null + "lc_collate": "en-u-kn-true" } }, "rlspolicies": {} diff --git a/packages/schemainspect/test/__snapshots__/everything.json b/packages/schemainspect/test/__snapshots__/everything.json index 8fdade0a..72edf8d6 100644 --- a/packages/schemainspect/test/__snapshots__/everything.json +++ b/packages/schemainspect/test/__snapshots__/everything.json @@ -29,7 +29,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "description": { @@ -45,7 +45,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "status": { @@ -61,7 +61,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -126,7 +126,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "order_id": { @@ -142,7 +142,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "quantity": { @@ -158,7 +158,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -187,7 +187,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "shipping_address": { @@ -203,7 +203,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "status": { @@ -227,7 +227,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "status2": { @@ -243,7 +243,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "h": { @@ -259,7 +259,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -288,7 +288,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "name": { @@ -304,7 +304,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "price": { @@ -320,7 +320,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "x": { @@ -336,7 +336,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "newcolumn": { @@ -352,7 +352,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "newcolumn2": { @@ -368,7 +368,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -399,7 +399,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -430,7 +430,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -461,7 +461,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "description": { @@ -477,7 +477,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "status": { @@ -493,7 +493,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -558,7 +558,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "order_id": { @@ -574,7 +574,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "quantity": { @@ -590,7 +590,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -619,7 +619,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "shipping_address": { @@ -635,7 +635,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "status": { @@ -659,7 +659,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "status2": { @@ -675,7 +675,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "h": { @@ -691,7 +691,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -720,7 +720,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "name": { @@ -736,7 +736,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "price": { @@ -752,7 +752,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "x": { @@ -768,7 +768,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "newcolumn": { @@ -784,7 +784,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "newcolumn2": { @@ -800,7 +800,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -829,7 +829,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -858,7 +858,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -961,7 +961,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "description": { @@ -977,7 +977,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "status": { @@ -993,7 +993,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -1058,7 +1058,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "order_id": { @@ -1074,7 +1074,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "quantity": { @@ -1090,7 +1090,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -1119,7 +1119,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "shipping_address": { @@ -1135,7 +1135,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "status": { @@ -1159,7 +1159,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "status2": { @@ -1175,7 +1175,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "h": { @@ -1191,7 +1191,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -1220,7 +1220,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "name": { @@ -1236,7 +1236,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "price": { @@ -1252,7 +1252,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "x": { @@ -1268,7 +1268,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "newcolumn": { @@ -1284,7 +1284,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false }, "newcolumn2": { @@ -1300,7 +1300,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -1329,7 +1329,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -1358,7 +1358,7 @@ "is_identity": false, "is_identity_always": false, "is_generated": false, - "can_drop_generated": false, + "can_drop_generated": true, "is_inherited": false } }, @@ -1504,17 +1504,17 @@ "plpgsql": { "name": "plpgsql", "schema": "pg_catalog", - "version": "1.0" + "version": "1.x" }, "citext": { "name": "citext", "schema": "public", - "version": "1.6" + "version": "1.x" }, "hstore": { "name": "hstore", "schema": "public", - "version": "1.8" + "version": "1.x" } }, "functions": { diff --git a/packages/schemainspect/test/json.test.ts b/packages/schemainspect/test/json.test.ts index a5c27fa4..d7646aaa 100644 --- a/packages/schemainspect/test/json.test.ts +++ b/packages/schemainspect/test/json.test.ts @@ -27,11 +27,17 @@ test.sequential.each([['collations'], ['everything']] as const)( const inspector = await PostgreSQL.create(client) - const json = JSON.parse(JSON.stringify(inspector.toJSON())) + const reviver = (key: string, value: any) => { + if (key === 'version' && typeof value === 'string') { + return value.replaceAll(/\.\d+/g, '.x') + } + return value + } + const json = JSON.parse(JSON.stringify(inspector.toJSON(), reviver)) const clone = PostgreSQL.fromJSON(json) - expect(JSON.parse(JSON.stringify(clone.toJSON()))).toEqual(json) + expect(JSON.parse(JSON.stringify(clone.toJSON(), reviver))).toEqual(json) const code = `${JSON.stringify(json, null, 2)}\n` await expect(code).toMatchFileSnapshot(`./__snapshots__/${name}.json`) diff --git a/packages/typegen/package.json b/packages/typegen/package.json index b8bbbb6c..dcd31407 100644 --- a/packages/typegen/package.json +++ b/packages/typegen/package.json @@ -31,7 +31,7 @@ "dependencies": { "@pgkit/client": "workspace:^", "@rebundled/execa": "8.0.2-next.0", - "@rebundled/p-memoize": "7.1.2-next.4", + "@rebundled/p-memoize": "7.1.2-next.8", "chokidar": "^3.5.3", "execa": "^5.1.1", "find-up": "^5.0.0", @@ -42,7 +42,7 @@ "neverthrow": "^7.1.0", "pgsql-ast-parser": "^11.1.0", "pluralize": "^8.0.0", - "trpc-cli": "https://pkg.pr.new/mmkal/trpc-cli@38", + "trpc-cli": "^0.5.0", "zod": "^3.23.8" }, "devDependencies": { diff --git a/packages/typegen/src/generate.ts b/packages/typegen/src/generate.ts index 3219b660..131825b0 100644 --- a/packages/typegen/src/generate.ts +++ b/packages/typegen/src/generate.ts @@ -4,7 +4,7 @@ import chokidar = require('chokidar') import * as fs from 'fs' import {glob} from 'glob' import * as lodash from 'lodash' -import memoizee = require('memoizee') +import memoizee = require('memoizee') // todo: replace with p-memoize import * as neverthrow from 'neverthrow' import * as path from 'path' import * as defaults from './defaults' @@ -13,7 +13,7 @@ import {getEnumTypes, getRegtypeToPgTypnameMapping, psqlClient} from './pg' import {getColumnInfo, getTypeability} from './query' import {getParameterTypes} from './query/parameters' import {ExtractedQuery, Options, QueryField, QueryParameter} from './types' -import {changedFiles, checkClean, containsIgnoreComment, globList} from './util' +import {changedFiles, checkClean, containsIgnoreComment, globList, promiseDotAllChunked} from './util' export type {Options} from './types' @@ -59,7 +59,6 @@ export const generate = async (inputOptions: Partial) => { }) } - // const psql = memoizee(_psql, {max: 1000}) const gdesc = memoizee(_gdesc, {max: 1000}) const getLogPath = (filepath: string) => { @@ -70,12 +69,13 @@ export const generate = async (inputOptions: Partial) => { const getFields = async (query: {sql: string}, searchPath?: string) => { const rowsResult = await gdesc(query.sql, searchPath) return rowsResult.asyncMap(async rows => { - return Promise.all( - rows.map>(async row => ({ + return promiseDotAllChunked( + rows, + async (row): Promise => ({ name: row.Column, regtype: row.Type, typescript: getTypeScriptType(row.Type), - })), + }), ) }) } @@ -85,14 +85,12 @@ export const generate = async (inputOptions: Partial) => { const getParameters = async (query: ExtractedQuery): Promise => { const regtypes = await getParameterTypes(pool, query.sql) - const promises = regtypes.map(async (regtype, i) => ({ + return promiseDotAllChunked(regtypes, async (regtype, i) => ({ name: `$${i + 1}`, // todo: parse query and use heuristic to get sensible names regtype, // todo(one day): handle arrays and other more complex types. Right now they'll fall back to `defaultType` (= `any` or `unknown`) typescript: getTypeScriptType(regtype), })) - - return Promise.all(promises) } const regtypeToPGTypeDictionary = await getRegtypeToPgTypnameMapping(pool) @@ -158,7 +156,11 @@ export const generate = async (inputOptions: Partial) => { } async function generateForFiles(files: string[]) { - const processedFiles = await Promise.all(files.map(generateForFile)) + // NOTE: empirically, when running this on a real codebase, upping the chunkSize here introduces some weird race condition, and the query results get mixed up half the time. + // That's on a codebase with only 2 files with queries, and 1 query in each, so likely the race condition is not hard to repro. It would probably be far worse on a larger codebase. + // So, in the spirit of make it work, make it right, make it fast, start with the default chunk size of 1. + // Ways it got mixed up: sometimes, "unnamed prepared statement does not exist", sometimes, result types were applied to the wrong queries. + const processedFiles = await promiseDotAllChunked(files, generateForFile) const stats = { 'Total files': processedFiles.length, @@ -183,12 +185,10 @@ export const generate = async (inputOptions: Partial) => { const queriesToDescribe = queries.filter(({sql}) => !containsIgnoreComment(sql)) const ignoreCount = queries.length - queriesToDescribe.length - const analysedQueryResults = await Promise.all( - queriesToDescribe.map(async query => { - const describedQuery = await describeQuery(query) - return describedQuery.asyncMap(dq => getColumnInfo(pool, dq, getTypeScriptType)) - }), - ) + const analysedQueryResults = await promiseDotAllChunked(queriesToDescribe, async query => { + const describedQuery = await describeQuery(query) + return describedQuery.asyncMap(dq => getColumnInfo(pool, dq, getTypeScriptType)) + }) const successfuls = analysedQueryResults.flatMap(res => { if (res.isOk()) return [res.value] @@ -208,6 +208,7 @@ export const generate = async (inputOptions: Partial) => { await options.writeTypes(analysedQueryResults.flatMap(res => (res.isOk() ? [res.value] : []))) } return { + file, total: queries.length, successful: successfuls.length, ignored: ignoreCount, diff --git a/packages/typegen/src/query/column-info.ts b/packages/typegen/src/query/column-info.ts index 0cfa4f36..6224d87b 100644 --- a/packages/typegen/src/query/column-info.ts +++ b/packages/typegen/src/query/column-info.ts @@ -30,7 +30,7 @@ type RegTypeToTypeScript = (formattedRegType: string & {brand?: 'formatted regty // todo: get table description from obj_description(oid) (like column) const logTestWarning = (...args: unknown[]) => { - if (process.env.NODE_ENV === 'test' || process.env.PGKIT_TYPEGEN_DEBUG) { + if (process.env.PGKIT_TYPEGEN_DEBUG) { // eslint-disable-next-line no-console console.warn(...args) } diff --git a/packages/typegen/src/util.ts b/packages/typegen/src/util.ts index e1acb1b1..3d02083c 100644 --- a/packages/typegen/src/util.ts +++ b/packages/typegen/src/util.ts @@ -95,11 +95,27 @@ export const containsIgnoreComment = (() => { return (query: string) => ignoreKeywords.test(query) })() -export const promiseDotOneAtATime = async (list: T[], fn: (item: T) => Promise) => { +/** + * Like `Promise.all`, but processes items in chunks to avoid parallelism issues, or more likely to avoid me worrying about parallelism issues. + * My worry should be resolved one way or another, but changing the default chunk size is easy vs huntin down all `Promise.all` usage. + */ +export const promiseDotAllChunked = async ( + list: T[], + fn: (item: T, index: number) => Promise, + {chunkSize = 1} = {}, +) => { const results: U[] = [] - for (const item of list) { - const result = await fn(item) - results.push(result) + for (let i = 0; i < list.length; i += chunkSize) { + const chunk = list.slice(i, i + chunkSize) + const chunkResults = await Promise.all(chunk.map((item, chunkIndex) => fn(item, i + chunkIndex))) + results.push(...chunkResults) } return results } + +export const test = () => { + const arr = ['a', 'b'] + return promiseDotAllChunked(arr, async item => { + return item satisfies string + }) +} diff --git a/packages/typegen/src/utils/memoize.ts b/packages/typegen/src/utils/memoize.ts index aa7a5026..3117a69d 100644 --- a/packages/typegen/src/utils/memoize.ts +++ b/packages/typegen/src/utils/memoize.ts @@ -1,8 +1,5 @@ import type {Client} from '@pgkit/client' - -// @ts-expect-error types are all messed up -// eslint-disable-next-line @typescript-eslint/no-var-requires -const {pMemoize} = require('@rebundled/p-memoize') as typeof import('@rebundled/p-memoize') +import {pMemoize} from '@rebundled/p-memoize' export const memoizeQueryFn = Promise>(fn: Fn) => { return pMemoize(fn, { diff --git a/packages/typegen/src/write/index.ts b/packages/typegen/src/write/index.ts index 05b1081a..b362cdb3 100644 --- a/packages/typegen/src/write/index.ts +++ b/packages/typegen/src/write/index.ts @@ -12,12 +12,16 @@ export type WriteFile = (filepath: string, content: string) => Promise export const defaultWriteFile: WriteFile = async (filepath, content) => { await fs.promises.mkdir(path.dirname(filepath), {recursive: true}) - const pretty = await prettifyOne({filepath, content}) - const existing = (await fs.promises.readFile(filepath).catch(() => null)) as string | null - if (existing === pretty) { + const newPretty = await prettifyOne({filepath, content}) + const existingContent = await fs.promises.readFile(filepath, 'utf8').catch(() => null /* file doesn't exist */) + if (existingContent === newPretty) { return } - await fs.promises.writeFile(filepath, pretty) + const existingPretty = existingContent && (await prettifyOne({filepath, content: existingContent})) + if (existingPretty === newPretty) { + return + } + await fs.promises.writeFile(filepath, newPretty) } /** diff --git a/packages/typegen/src/write/inline.ts b/packages/typegen/src/write/inline.ts index d4dd1623..accada6f 100644 --- a/packages/typegen/src/write/inline.ts +++ b/packages/typegen/src/write/inline.ts @@ -39,6 +39,7 @@ const applyEdits = (input: string, edits: Edit[]) => export function getFileWriter({getQueriesModulePath = defaultGetQueriesModule, writeFile}: WriteTSFileOptions) { return async (group: TaggedQuery[], file: string) => { + // eslint-disable-next-line const ts: typeof import('typescript') = require('typescript') const originalSource = group[0].source const sourceFile = ts.createSourceFile(file, originalSource, ts.ScriptTarget.ES2015, /* setParentNodes */ true) diff --git a/packages/typegen/src/write/prettify.ts b/packages/typegen/src/write/prettify.ts index a2654f14..ae261522 100644 --- a/packages/typegen/src/write/prettify.ts +++ b/packages/typegen/src/write/prettify.ts @@ -1,26 +1,11 @@ import * as findUp from 'find-up' -export async function prettifyOne({filepath, content}: {filepath: string; content: string}) { - try { - // eslint-disable-next-line mmkal/import/no-extraneous-dependencies - const prettier: typeof import('prettier') = require('prettier') - const rcFile = findUp.sync('.prettierrc.js') - const rc = rcFile && require(rcFile) - return await prettier.format(content, {filepath, ...rc}) - } catch (e: any) { - const help = - e.code === 'MODULE_NOT_FOUND' ? `Install prettier to fix this. ${e.message}` : `Error below:\n${e.message}` - console.warn(`prettier failed to run; Your output might be ugly! ${help}`) - } - - return content -} - /** * Uses typescript to prettify some code by parsing it into an AST then re-printing it. This will look OK but probably * won't conform to your lint rules. It also may or may not work if there are syntax errors. */ export const tsPrettify = (uglyContent: string) => { + // eslint-disable-next-line const ts: typeof import('typescript') = require('typescript') const sourceFile = ts.createSourceFile('', uglyContent, ts.ScriptTarget.ES2015, true) const prettyContent = ts.createPrinter().printNode(ts.EmitHint.SourceFile, sourceFile, sourceFile) @@ -30,3 +15,28 @@ export const tsPrettify = (uglyContent: string) => { .replaceAll(/(\*\/\r?\n)\r?\n/g, '$1') .replaceAll(/\*\/(\r?\n)\r?\n/g, '$1') // typescript printer squashes everything a bit too much } + +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable no-console */ +/* eslint-disable import-x/no-extraneous-dependencies */ +/* eslint-disable @typescript-eslint/no-require-imports */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ + +export async function prettifyOne({filepath, content}: {filepath: string; content: string}) { + if (typeof content !== 'string') throw new Error('content is not a string: ' + typeof content) + try { + const prettier: typeof import('prettier') = require('prettier') + const rcFile = findUp.sync('.prettierrc.js') + const rc = rcFile && (require(rcFile) as {}) + return await prettier.format(content, {filepath, ...rc}) + } catch (e: any) { + let message = `prettier failed to run; Your output might be ugly!` + if (e.code === 'MODULE_NOT_FOUND') { + message += ` Install prettier to fix this. ${e.message}` + } + const wrapped = new Error(message, {cause: e}) + console.warn(wrapped) + } + + return content +} diff --git a/packages/typegen/test/helper.ts b/packages/typegen/test/helper.ts index dd0cf4ec..9632fcf6 100644 --- a/packages/typegen/test/helper.ts +++ b/packages/typegen/test/helper.ts @@ -80,10 +80,10 @@ export const getPoolHelper = (params: {__filename: string; baseConnectionURI: st // afterAll(async () => new Promise(r => setTimeout(r, 1))) const setupDb = async () => { - const exists = Boolean(await admin.maybeOne(sql`select 1 from pg_database where datname = ${dbName}`)) - if (!exists) { - await admin.query(sql`create database ${sql.identifier([dbName])}`) - } + await admin.query(sql`create database ${sql.identifier([dbName])}`).catch(e => { + if (e.message.includes('unique_violation') || e.cause?.message.match(/database ".+" already exists/)) return + throw e + }) await pool.query(sql` drop schema if exists ${schemaIdentifier} cascade; diff --git a/packages/typegen/test/limitations.test.ts b/packages/typegen/test/limitations.test.ts index 7c386647..e8c66cfa 100644 --- a/packages/typegen/test/limitations.test.ts +++ b/packages/typegen/test/limitations.test.ts @@ -132,9 +132,9 @@ test('duplicate columns', async () => { * * regtype: \`integer\` * - * regtype: \`text\` + * regtype: \`integer\` */ - a: (number | null) | (string | null) + a: number | null } } " diff --git a/packages/typegen/test/options.test.ts b/packages/typegen/test/options.test.ts index e0a05445..a2f3c861 100644 --- a/packages/typegen/test/options.test.ts +++ b/packages/typegen/test/options.test.ts @@ -519,8 +519,8 @@ test('ignore irrelevant syntax', async () => { /** - query: \`select 1\` */ export interface Column { - /** not null: \`true\`, regtype: \`integer\` */ - '?column?': number + /** regtype: \`integer\` */ + '?column?': number | null } } " diff --git a/packages/typegen/test/primitives.test.ts b/packages/typegen/test/primitives.test.ts index bce7c721..d9aa8e72 100644 --- a/packages/typegen/test/primitives.test.ts +++ b/packages/typegen/test/primitives.test.ts @@ -62,7 +62,7 @@ test('primitives are non-nullable', async () => { sql\`select sum(b) from test_table\`, sql\`select current_date\`, sql\`select 'foo' || 'bar'\`, - sql\`select 'foo' || null\`, + sql\`select 'foo' || null\`, sql\`select a > 1 from test_table\`, sql\`select 2 > 1 as a\`, ] @@ -106,18 +106,16 @@ test('primitives are non-nullable', async () => { /** - query: \`select current_date\` */ export interface CurrentDate { - /** not null: \`true\`, regtype: \`date\` */ - current_date: Date + /** regtype: \`date\` */ + current_date: Date | null } - /** - query: \`select 'foo' || 'bar'\` */ + /** + * queries: + * - \`select 'foo' || 'bar'\` + * - \`select 'foo' || null\` + */ export interface Column { - /** not null: \`true\`, regtype: \`text\` */ - '?column?': string - } - - /** - query: \`select 'foo' || null\` */ - export interface Column_4 { /** regtype: \`text\` */ '?column?': string | null } diff --git a/packages/typegen/test/type-mappings.test.ts b/packages/typegen/test/type-mappings.test.ts index 4b7c2881..ee8a01ae 100644 --- a/packages/typegen/test/type-mappings.test.ts +++ b/packages/typegen/test/type-mappings.test.ts @@ -91,7 +91,7 @@ test(`default type mappings`, async () => { /** regtype: \`time without time zone\` */ f: string | null - /** regtype: \`bit(1)\` */ + /** regtype: \`bit\` */ g: number | null /** regtype: \`money\` */ @@ -109,7 +109,7 @@ test(`default type mappings`, async () => { /** regtype: \`real\` */ l: number | null - /** regtype: \`character(1)\` */ + /** regtype: \`character\` */ m: string | null /** regtype: \`character varying\` */ diff --git a/packages/typegen/test/ugly.test.ts b/packages/typegen/test/ugly.test.ts index eaefde69..f3e096b2 100644 --- a/packages/typegen/test/ugly.test.ts +++ b/packages/typegen/test/ugly.test.ts @@ -47,7 +47,7 @@ test('prettier is optional', async () => { expect(mockWarn).toBeCalledTimes(1) expect(mockWarn.mock.calls[0]).toMatchInlineSnapshot(` [ - "prettier failed to run; Your output might be ugly! Install prettier to fix this. prettier not found", + [Error: prettier failed to run; Your output might be ugly! Install prettier to fix this. prettier not found], ] `) @@ -99,8 +99,7 @@ test('prettier can fail', async () => { expect(mockWarn).toBeCalledTimes(1) expect(mockWarn.mock.calls[0]).toMatchInlineSnapshot(` [ - "prettier failed to run; Your output might be ugly! Error below: - Syntax error on line 1234", + [Error: prettier failed to run; Your output might be ugly!], ] `) diff --git a/packages/typegen/test/watch.test.ts b/packages/typegen/test/watch.test.ts index 96e8bb97..99c8f30c 100644 --- a/packages/typegen/test/watch.test.ts +++ b/packages/typegen/test/watch.test.ts @@ -2,7 +2,7 @@ import * as fs from 'fs' import * as fsSyncer from 'fs-syncer' import * as path from 'path' -import {test, beforeEach, expect, vi as jest} from 'vitest' +import {test as _test, beforeEach, expect, vi as jest} from 'vitest' import * as typegen from '../src' import {getPureHelper as getHelper} from './helper' @@ -14,157 +14,167 @@ beforeEach(async () => { await helper.setupDb() }) -test('watch file system', async () => { - const syncer = fsSyncer.testFixture({ - expect, - targetState: { - 'file1.ts': ` +const test = process.env.CI ? _test.skip : _test + +test( + 'watch file system', + async () => { + const syncer = fsSyncer.testFixture({ + expect, + targetState: { + 'file1.ts': ` + import {sql} from '@pgkit/client' + export default sql\`select 123 as abc\` + `, + 'file2.ts': ` + import {sql} from '@pgkit/client' + export default sql\`select 123 as xyz\` + `, + }, + }) + + syncer.sync() + + const {watch} = await typegen.generate(typegenOptions(syncer.baseDir)) + + expect(syncer.yaml()).toMatchInlineSnapshot(` + "--- + file1.ts: |- import {sql} from '@pgkit/client' - export default sql\`select 123 as abc\` - `, - 'file2.ts': ` - import {sql} from '@pgkit/client' - export default sql\`select 123 as xyz\` - `, - }, - }) - - syncer.sync() + export default sql\`select 123 as abc\` - const {watch} = await typegen.generate(typegenOptions(syncer.baseDir)) + export declare namespace queries { + // Generated by @pgkit/typegen - expect(syncer.yaml()).toMatchInlineSnapshot(` - "--- - file1.ts: |- - import {sql} from '@pgkit/client' - export default sql\`select 123 as abc\` - - export declare namespace queries { - // Generated by @pgkit/typegen - - /** - query: \`select 123 as abc\` */ - export interface Abc { - /** not null: \`true\`, regtype: \`integer\` */ - abc: number + /** - query: \`select 123 as abc\` */ + export interface Abc { + /** not null: \`true\`, regtype: \`integer\` */ + abc: number + } } - } - file2.ts: |- - import {sql} from '@pgkit/client' - export default sql\`select 123 as xyz\` + file2.ts: |- + import {sql} from '@pgkit/client' + export default sql\`select 123 as xyz\` - export declare namespace queries { - // Generated by @pgkit/typegen + export declare namespace queries { + // Generated by @pgkit/typegen - /** - query: \`select 123 as xyz\` */ - export interface Xyz { - /** not null: \`true\`, regtype: \`integer\` */ - xyz: number - } - } - " - `) - - const watcher = watch() - - await waitForLog(/Watching for file changes/) - await waitForLog(/Initial codegen complete/) - fs.writeFileSync( - path.join(syncer.baseDir, 'file1.ts'), - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - syncer.read()['file1.ts'].replace('select 123 as abc', 'select 123 as def'), - ) - await waitForLog(/file1.ts was changed, running codegen/) - - await watcher.close() - - expect(syncer.yaml()).toMatchInlineSnapshot(` - "--- - file1.ts: |- - import {sql} from '@pgkit/client' - export default sql\`select 123 as def\` - - export declare namespace queries { - // Generated by @pgkit/typegen - - /** - query: \`select 123 as def\` */ - export interface Def { - /** not null: \`true\`, regtype: \`integer\` */ - def: number + /** - query: \`select 123 as xyz\` */ + export interface Xyz { + /** not null: \`true\`, regtype: \`integer\` */ + xyz: number + } } - } - - file2.ts: |- - import {sql} from '@pgkit/client' - export default sql\`select 123 as xyz\` + " + `) - export declare namespace queries { - // Generated by @pgkit/typegen + const watcher = watch() - /** - query: \`select 123 as xyz\` */ - export interface Xyz { - /** not null: \`true\`, regtype: \`integer\` */ - xyz: number - } - } - " - `) + await waitForLog(/Watching for file changes/) + await waitForLog(/Initial codegen complete/) + fs.writeFileSync( + path.join(syncer.baseDir, 'file1.ts'), + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + syncer.read()['file1.ts'].replace('select 123 as abc', 'select 123 as def'), + ) + await waitForLog(/file1.ts was changed, running codegen/) - // below assertion fails - bug in chokidar? - // const watchLogs = JSON.stringify(logger.info.mock.calls, null, 2) - // expect(watchLogs, `logs: ${watchLogs}`).not.toContain('file2.ts') -}) + await watcher.close() -test('lazily watch file system', async () => { - const syncer = fsSyncer.testFixture({ - expect, - targetState: { - 'file1.ts': ` + expect(syncer.yaml()).toMatchInlineSnapshot(` + "--- + file1.ts: |- import {sql} from '@pgkit/client' - export default sql\`select 123 as abc\` - `, - }, - }) - - syncer.sync() - - const {watch} = await typegen.generate({ - ...typegenOptions(syncer.baseDir), - lazy: true, - }) + export default sql\`select 123 as def\` - expect(logger.info).toHaveBeenCalledTimes(1) + export declare namespace queries { + // Generated by @pgkit/typegen - const watcher = watch() + /** - query: \`select 123 as def\` */ + export interface Def { + /** not null: \`true\`, regtype: \`integer\` */ + def: number + } + } - await waitForLog(/Watching for file changes/) - fs.writeFileSync( - path.join(syncer.baseDir, 'file1.ts'), - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - syncer.read()['file1.ts'].replace('select 123 as abc', 'select 123 as def'), - ) - await waitForLog(/file1.ts was changed, running codegen/) + file2.ts: |- + import {sql} from '@pgkit/client' + export default sql\`select 123 as xyz\` - await watcher.close() + export declare namespace queries { + // Generated by @pgkit/typegen - expect(syncer.yaml()).toMatchInlineSnapshot(` - "--- - file1.ts: |- - import {sql} from '@pgkit/client' - export default sql\`select 123 as def\` + /** - query: \`select 123 as xyz\` */ + export interface Xyz { + /** not null: \`true\`, regtype: \`integer\` */ + xyz: number + } + } + " + `) + + // below assertion fails - bug in chokidar? + // const watchLogs = JSON.stringify(logger.info.mock.calls, null, 2) + // expect(watchLogs, `logs: ${watchLogs}`).not.toContain('file2.ts') + }, + {retry: 3}, +) + +test( + 'lazily watch file system', + async () => { + const syncer = fsSyncer.testFixture({ + expect, + targetState: { + 'file1.ts': ` + import {sql} from '@pgkit/client' + export default sql\`select 123 as abc\` + `, + }, + }) + + syncer.sync() + + const {watch} = await typegen.generate({ + ...typegenOptions(syncer.baseDir), + lazy: true, + }) + + expect(logger.info).toHaveBeenCalledTimes(1) + + const watcher = watch() + + await waitForLog(/Watching for file changes/) + fs.writeFileSync( + path.join(syncer.baseDir, 'file1.ts'), + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + syncer.read()['file1.ts'].replace('select 123 as abc', 'select 123 as def'), + ) + await waitForLog(/file1.ts was changed, running codegen/) + + await watcher.close() + + expect(syncer.yaml()).toMatchInlineSnapshot(` + "--- + file1.ts: |- + import {sql} from '@pgkit/client' + export default sql\`select 123 as def\` - export declare namespace queries { - // Generated by @pgkit/typegen + export declare namespace queries { + // Generated by @pgkit/typegen - /** - query: \`select 123 as def\` */ - export interface Def { - /** not null: \`true\`, regtype: \`integer\` */ - def: number + /** - query: \`select 123 as def\` */ + export interface Def { + /** not null: \`true\`, regtype: \`integer\` */ + def: number + } } - } - " - `) -}) + " + `) + }, + {retry: 3}, +) const waitForLog = async (match: RegExp, timeoutMs = 3000) => { return new Promise((resolve, reject) => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a39046d5..c49e6fba 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,7 +13,7 @@ importers: version: 2.0.2(enquirer@2.4.1) '@parcel/transformer-typescript-types': specifier: 2.11.0 - version: 2.11.0(@parcel/core@2.11.0)(typescript@5.3.3) + version: 2.11.0(@parcel/core@2.11.0)(typescript@5.6.2) '@rebundled/execa': specifier: 8.0.2-next.0 version: 8.0.2-next.0 @@ -45,14 +45,14 @@ importers: specifier: ^4.19.0 version: 4.19.0 turbo: - specifier: ^1.12.3 - version: 1.12.3 + specifier: ^2.1.2 + version: 2.1.2 type-fest: specifier: ^4.10.3 version: 4.10.3 typescript: - specifier: '>=3.0.0' - version: 5.3.3 + specifier: ^5.6.2 + version: 5.6.2 util: specifier: ^0.12.3 version: 0.12.5 @@ -197,7 +197,7 @@ importers: version: 6.4.6 eslint-plugin-mmkal: specifier: 0.10.1 - version: 0.10.1(eslint@8.57.0)(typescript@5.3.3)(vitest@1.2.2(@types/node@20.11.17)(sass@1.71.0)) + version: 0.10.1(eslint@8.57.0)(typescript@5.6.2)(vitest@1.2.2(@types/node@20.11.17)(sass@1.71.0)) express: specifier: ^4.18.2 version: 4.18.2 @@ -254,7 +254,7 @@ importers: version: 2.3.0 tailwindcss-animate: specifier: ^1.0.7 - version: 1.0.7(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.11.17)(typescript@5.3.3))) + version: 1.0.7(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.11.17)(typescript@5.6.2))) websql-autocomplete: specifier: ^6.0.0 version: 6.1.0(antlr4ng-cli@1.0.7) @@ -285,28 +285,28 @@ importers: version: 8.57.0 eslint-plugin-tailwindcss: specifier: 3.15.1 - version: 3.15.1(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.11.17)(typescript@5.3.3))) + version: 3.15.1(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.11.17)(typescript@5.6.2))) postcss: specifier: ^8.4.38 version: 8.4.38 shadcn-ui: specifier: 0.8.0 - version: 0.8.0(typescript@5.3.3) + version: 0.8.0(typescript@5.6.2) strip-indent: specifier: ^4.0.0 version: 4.0.0 tailwindcss: specifier: ^3.4.3 - version: 3.4.3(ts-node@10.9.2(@types/node@20.11.17)(typescript@5.3.3)) + version: 3.4.3(ts-node@10.9.2(@types/node@20.11.17)(typescript@5.6.2)) tsx: specifier: ^4.19.0 version: 4.19.0 typescript: - specifier: ^5.2.2 - version: 5.3.3 + specifier: ^5.6.2 + version: 5.6.2 typescript-eslint: specifier: ^7.1.0 - version: 7.1.0(eslint@8.57.0)(typescript@5.3.3) + version: 7.1.0(eslint@8.57.0)(typescript@5.6.2) vite: specifier: ^5.0.0 version: 5.1.1(@types/node@20.11.17)(sass@1.71.0) @@ -442,8 +442,8 @@ importers: specifier: ^10.45.2 version: 10.45.2 trpc-cli: - specifier: ^0.2.1 - version: 0.2.1(@trpc/server@10.45.2)(zod@3.23.8) + specifier: ^0.5.0 + version: 0.5.0(@trpc/server@10.45.2)(zod@3.23.8) zod: specifier: ^3.23.8 version: 3.23.8 @@ -512,8 +512,8 @@ importers: specifier: ^2.0.1 version: 2.0.1 trpc-cli: - specifier: ^0.3.0 - version: 0.3.0(@trpc/server@10.45.2)(zod@3.23.8) + specifier: ^0.5.0 + version: 0.5.0(@trpc/server@10.45.2)(zod@3.23.8) umzug: specifier: ^3.7.0 version: 3.7.0 @@ -597,8 +597,8 @@ importers: specifier: ^0.4.4 version: 0.4.4 trpc-cli: - specifier: https://pkg.pr.new/mmkal/trpc-cli@38 - version: https://pkg.pr.new/mmkal/trpc-cli@38(@trpc/server@10.45.2)(zod@3.23.8) + specifier: ^0.5.0 + version: 0.5.0(@trpc/server@10.45.2)(zod@3.23.8) devDependencies: '@types/express': specifier: ^4.17.21 @@ -650,8 +650,8 @@ importers: specifier: 8.0.2-next.0 version: 8.0.2-next.0 '@rebundled/p-memoize': - specifier: 7.1.2-next.4 - version: 7.1.2-next.4 + specifier: 7.1.2-next.8 + version: 7.1.2-next.8 chokidar: specifier: ^3.5.3 version: 3.6.0 @@ -683,11 +683,11 @@ importers: specifier: ^8.0.0 version: 8.0.0 trpc-cli: - specifier: https://pkg.pr.new/mmkal/trpc-cli@38 - version: https://pkg.pr.new/mmkal/trpc-cli@38(@trpc/server@10.45.2)(zod@3.23.8) + specifier: ^0.5.0 + version: 0.5.0(@trpc/server@10.45.2)(zod@3.23.8) typescript: specifier: '*' - version: 5.3.3 + version: 5.6.2 zod: specifier: ^3.23.8 version: 3.23.8 @@ -712,7 +712,7 @@ importers: version: 8.57.0 eslint-plugin-mmkal: specifier: 0.10.1 - version: 0.10.1(eslint@8.57.0)(typescript@5.3.3)(vitest@1.2.2(@types/node@20.12.12)(sass@1.71.0)) + version: 0.10.1(eslint@8.57.0)(typescript@5.6.2)(vitest@1.2.2(@types/node@20.12.12)(sass@1.71.0)) expect-type: specifier: ^0.17.3 version: 0.17.3 @@ -756,8 +756,8 @@ importers: specifier: ^2.8.0 version: 2.8.0 trpc-cli: - specifier: https://pkg.pr.new/mmkal/trpc-cli@38 - version: https://pkg.pr.new/mmkal/trpc-cli@38(@trpc/server@10.45.2)(zod@3.23.8) + specifier: ^0.5.0 + version: 0.5.0(@trpc/server@10.45.2)(zod@3.23.8) devDependencies: '@types/semver': specifier: ^7.5.8 @@ -2303,8 +2303,8 @@ packages: resolution: {integrity: sha512-S3rd39mObqtVaXqShfiSiYf4F1pUjSXyONfk+VRvsrQt2i1pFTZZIEm7yGyplzPcWefY8/dA/XSivCMVkMGMHQ==} engines: {node: '>=18'} - '@rebundled/p-memoize@7.1.2-next.4': - resolution: {integrity: sha512-WvuM+9LAkignqZNgkWUlROTW9CFBj5ENVFIpPZkKis/vukUqQLiIZG63QT6nBxOoYfw+Yx707zv6iTXGN1Zp+Q==} + '@rebundled/p-memoize@7.1.2-next.8': + resolution: {integrity: sha512-m9Fq4wwjAGi1fM6YIYvGGCLmCrMuYQ8yyd6JDb1hmmTasziUDsdJoqyUQp7Mz2fwd0YuHfPPIWg6C2kbA8j//w==} engines: {node: '>=14.16'} '@rollup/rollup-android-arm-eabi@4.9.6': @@ -7842,23 +7842,8 @@ packages: trough@2.2.0: resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} - trpc-cli@0.2.1: - resolution: {integrity: sha512-HJzmLbdbj2CcdP7XCj+TAWvVNSbnhiLIaRUI67v4qmpHJoXgHisseih4Kap6WvC/CVYvuAieB0TNV0etEIf44Q==} - engines: {node: '>=18'} - peerDependencies: - '@trpc/server': '>=10' - zod: '>=3' - - trpc-cli@0.3.0: - resolution: {integrity: sha512-FahEW3lt9L3eMVbDD3tpCkmxkDGnarfpC4vmJJWoIBy8sreAwbWY7meAhtKM4UrTbEk2Qq4Pg9hkySsu0Cio7w==} - engines: {node: '>=18'} - peerDependencies: - '@trpc/server': '>=10' - zod: '>=3' - - trpc-cli@https://pkg.pr.new/mmkal/trpc-cli@38: - resolution: {tarball: https://pkg.pr.new/mmkal/trpc-cli@38} - version: 0.4.1 + trpc-cli@0.5.0: + resolution: {integrity: sha512-/P0P+K/CANI5F2pOkP2rTekY+oRC1vs+6MUTq0uMPtnGJ3gKpsp4aYuDh+6+pGbGYYQU3JuExFirAzTFsQR4Lw==} engines: {node: '>=18'} peerDependencies: '@trpc/server': '>=10' @@ -7946,38 +7931,38 @@ packages: engines: {node: '>=18.0.0'} hasBin: true - turbo-darwin-64@1.12.3: - resolution: {integrity: sha512-dDglIaux+A4jOnB9CDH69sujmrnuLJLrKw1t3J+if6ySlFuxSwC++gDq9TVuOZo2+S7lFkGh+x5ytn3wp+jE8Q==} + turbo-darwin-64@2.1.2: + resolution: {integrity: sha512-3TEBxHWh99h2yIzkuIigMEOXt/ItYQp0aPiJjPd1xN4oDcsKK5AxiFKPH9pdtfIBzYsY59kQhZiFj0ELnSP7Bw==} cpu: [x64] os: [darwin] - turbo-darwin-arm64@1.12.3: - resolution: {integrity: sha512-5TqqeujEyHMoVUWGzSzUl5ERSg7HDCdbU3gBs5ziWTpFRpeJ/+Y15kYyZJcMQcubRIH3Y1hL/yA5IhlGdgXOMA==} + turbo-darwin-arm64@2.1.2: + resolution: {integrity: sha512-he0miWNq2WxJzsH82jS2Z4MXpnkzn9SH8a79iPXiJkq25QREImucscM4RPasXm8wARp91pyysJMq6aasD45CeA==} cpu: [arm64] os: [darwin] - turbo-linux-64@1.12.3: - resolution: {integrity: sha512-yUreU+/gq4vlBtcdyfjz7slwz4zM1RG8sSXvyHmAS+QXqSrGkegg4qLl2fRbv/c3EyA/XbfcZuD6tcrXkejr6g==} + turbo-linux-64@2.1.2: + resolution: {integrity: sha512-fKUBcc0rK8Vdqv5a/E3CSpMBLG1bzwv+Q0Q83F8fG2ZfNCNKGbcEYABdonNZkkx141Rj03cZQFCgxu3MVEGU+A==} cpu: [x64] os: [linux] - turbo-linux-arm64@1.12.3: - resolution: {integrity: sha512-XRwAsp2eRSqZmaMVNrmHoKqofeJMuD87zmefZLTRAObh38hIwKgyl2QRsJIbteob5RN77yFbv3lAJ36UIY5h7w==} + turbo-linux-arm64@2.1.2: + resolution: {integrity: sha512-sV8Bpmm0WiuxgbhxymcC7wSsuxfBBieI98GegSwbr/bs1ANAgzCg93urIrdKdQ3/b31zZxQwcaP4FBF1wx1Qdg==} cpu: [arm64] os: [linux] - turbo-windows-64@1.12.3: - resolution: {integrity: sha512-CPnRfnUCtmFeShOtUdMCthySjmyHaoTyh9JueiYFvtCNeO3WfDMj63dpOQstQWHdJFYmIrIGfhAclcds9ePQYA==} + turbo-windows-64@2.1.2: + resolution: {integrity: sha512-wcmIJZI9ORT9ykHGliFE6kWRQrlH930QGSjSgWC8uFChFFuOyUlvC7ttcxuSvU9VqC7NF4C+GVAcFJQ8lTjN7g==} cpu: [x64] os: [win32] - turbo-windows-arm64@1.12.3: - resolution: {integrity: sha512-cYA/wlzvp4vlCNHYJ2AjNS3FLXWwUC/5CJompBkTeKFFB6AviE/iLkbIhFikCVSNXZk/3AGanpMUXIkt3bdlwg==} + turbo-windows-arm64@2.1.2: + resolution: {integrity: sha512-zdnXjrhk7YO6CP+Q5wPueEvOCLH4lDa6C4rrwiakcWcPgcQGbVozJlo4uaQ6awo8HLWQEvOwu84RkWTdLAc/Hw==} cpu: [arm64] os: [win32] - turbo@1.12.3: - resolution: {integrity: sha512-a6q8I0TK9ohACYbkmxzG/JYPuDC4VCvfmXLTlf321qQ4BIAhoyaOj/O2g+zJ6L1vNYnZ82G4LrbMfgLLngbLsg==} + turbo@2.1.2: + resolution: {integrity: sha512-Jb0rbU4iHEVQ18An/YfakdIv9rKnd3zUfSE117EngrfWXFHo3RndVH96US3GsT8VHpwTncPePDBT2t06PaFLrw==} hasBin: true type-check@0.4.0: @@ -8091,11 +8076,6 @@ packages: typescript: optional: true - typescript@5.3.3: - resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} - engines: {node: '>=14.17'} - hasBin: true - typescript@5.6.2: resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==} engines: {node: '>=14.17'} @@ -9464,22 +9444,22 @@ snapshots: dependencies: detect-libc: 1.0.3 - '@parcel/transformer-typescript-types@2.11.0(@parcel/core@2.11.0)(typescript@5.3.3)': + '@parcel/transformer-typescript-types@2.11.0(@parcel/core@2.11.0)(typescript@5.6.2)': dependencies: '@parcel/diagnostic': 2.11.0 '@parcel/plugin': 2.11.0(@parcel/core@2.11.0) '@parcel/source-map': 2.1.1 - '@parcel/ts-utils': 2.11.0(typescript@5.3.3) + '@parcel/ts-utils': 2.11.0(typescript@5.6.2) '@parcel/utils': 2.11.0 nullthrows: 1.1.1 - typescript: 5.3.3 + typescript: 5.6.2 transitivePeerDependencies: - '@parcel/core' - '@parcel/ts-utils@2.11.0(typescript@5.3.3)': + '@parcel/ts-utils@2.11.0(typescript@5.6.2)': dependencies: nullthrows: 1.1.1 - typescript: 5.3.3 + typescript: 5.6.2 '@parcel/types@2.11.0(@parcel/core@2.11.0)': dependencies: @@ -10077,7 +10057,7 @@ snapshots: signal-exit: 4.1.0 strip-final-newline: 4.0.0 - '@rebundled/p-memoize@7.1.2-next.4': + '@rebundled/p-memoize@7.1.2-next.8': dependencies: mimic-fn: 4.0.0 type-fest: 3.13.1 @@ -10121,24 +10101,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.9.6': optional: true - '@rushstack/eslint-config@3.6.5(eslint@8.57.0)(typescript@5.3.3)': - dependencies: - '@rushstack/eslint-patch': 1.8.0 - '@rushstack/eslint-plugin': 0.15.1(eslint@8.57.0)(typescript@5.3.3) - '@rushstack/eslint-plugin-packlets': 0.9.1(eslint@8.57.0)(typescript@5.3.3) - '@rushstack/eslint-plugin-security': 0.8.1(eslint@8.57.0)(typescript@5.3.3) - '@typescript-eslint/eslint-plugin': 6.19.1(@typescript-eslint/parser@6.19.1(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3) - '@typescript-eslint/parser': 6.19.1(eslint@8.57.0)(typescript@5.3.3) - '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.3.3) - '@typescript-eslint/utils': 6.19.1(eslint@8.57.0)(typescript@5.3.3) - eslint: 8.57.0 - eslint-plugin-promise: 6.1.1(eslint@8.57.0) - eslint-plugin-react: 7.33.2(eslint@8.57.0) - eslint-plugin-tsdoc: 0.2.17 - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - '@rushstack/eslint-config@3.6.5(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@rushstack/eslint-patch': 1.8.0 @@ -10177,15 +10139,6 @@ snapshots: '@rushstack/eslint-patch@1.8.0': {} - '@rushstack/eslint-plugin-packlets@0.8.1(eslint@8.57.0)(typescript@5.3.3)': - dependencies: - '@rushstack/tree-pattern': 0.3.1 - '@typescript-eslint/experimental-utils': 5.59.11(eslint@8.57.0)(typescript@5.3.3) - eslint: 8.57.0 - transitivePeerDependencies: - - supports-color - - typescript - '@rushstack/eslint-plugin-packlets@0.8.1(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@rushstack/tree-pattern': 0.3.1 @@ -10204,15 +10157,6 @@ snapshots: - supports-color - typescript - '@rushstack/eslint-plugin-packlets@0.9.1(eslint@8.57.0)(typescript@5.3.3)': - dependencies: - '@rushstack/tree-pattern': 0.3.3 - '@typescript-eslint/utils': 6.19.1(eslint@8.57.0)(typescript@5.3.3) - eslint: 8.57.0 - transitivePeerDependencies: - - supports-color - - typescript - '@rushstack/eslint-plugin-packlets@0.9.1(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@rushstack/tree-pattern': 0.3.3 @@ -10231,15 +10175,6 @@ snapshots: - supports-color - typescript - '@rushstack/eslint-plugin-security@0.7.1(eslint@8.57.0)(typescript@5.3.3)': - dependencies: - '@rushstack/tree-pattern': 0.3.1 - '@typescript-eslint/experimental-utils': 5.59.11(eslint@8.57.0)(typescript@5.3.3) - eslint: 8.57.0 - transitivePeerDependencies: - - supports-color - - typescript - '@rushstack/eslint-plugin-security@0.7.1(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@rushstack/tree-pattern': 0.3.1 @@ -10258,15 +10193,6 @@ snapshots: - supports-color - typescript - '@rushstack/eslint-plugin-security@0.8.1(eslint@8.57.0)(typescript@5.3.3)': - dependencies: - '@rushstack/tree-pattern': 0.3.3 - '@typescript-eslint/utils': 6.19.1(eslint@8.57.0)(typescript@5.3.3) - eslint: 8.57.0 - transitivePeerDependencies: - - supports-color - - typescript - '@rushstack/eslint-plugin-security@0.8.1(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@rushstack/tree-pattern': 0.3.3 @@ -10285,15 +10211,6 @@ snapshots: - supports-color - typescript - '@rushstack/eslint-plugin@0.13.1(eslint@8.57.0)(typescript@5.3.3)': - dependencies: - '@rushstack/tree-pattern': 0.3.1 - '@typescript-eslint/experimental-utils': 5.59.11(eslint@8.57.0)(typescript@5.3.3) - eslint: 8.57.0 - transitivePeerDependencies: - - supports-color - - typescript - '@rushstack/eslint-plugin@0.13.1(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@rushstack/tree-pattern': 0.3.1 @@ -10312,15 +10229,6 @@ snapshots: - supports-color - typescript - '@rushstack/eslint-plugin@0.15.1(eslint@8.57.0)(typescript@5.3.3)': - dependencies: - '@rushstack/tree-pattern': 0.3.3 - '@typescript-eslint/utils': 6.19.1(eslint@8.57.0)(typescript@5.3.3) - eslint: 8.57.0 - transitivePeerDependencies: - - supports-color - - typescript - '@rushstack/eslint-plugin@0.15.1(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@rushstack/tree-pattern': 0.3.3 @@ -10733,26 +10641,6 @@ snapshots: '@types/yoga-layout@1.9.2': {} - '@typescript-eslint/eslint-plugin@6.19.1(@typescript-eslint/parser@6.19.1(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3)': - dependencies: - '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 6.19.1(eslint@8.57.0)(typescript@5.3.3) - '@typescript-eslint/scope-manager': 6.19.1 - '@typescript-eslint/type-utils': 6.19.1(eslint@8.57.0)(typescript@5.3.3) - '@typescript-eslint/utils': 6.19.1(eslint@8.57.0)(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 6.19.1 - debug: 4.3.4 - eslint: 8.57.0 - graphemer: 1.4.0 - ignore: 5.3.1 - natural-compare: 1.4.0 - semver: 7.6.0 - ts-api-utils: 1.3.0(typescript@5.3.3) - optionalDependencies: - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/eslint-plugin@6.19.1(@typescript-eslint/parser@6.19.1(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@eslint-community/regexpp': 4.10.0 @@ -10793,26 +10681,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@7.1.0(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3)': - dependencies: - '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3) - '@typescript-eslint/scope-manager': 7.1.0 - '@typescript-eslint/type-utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3) - '@typescript-eslint/utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 7.1.0 - debug: 4.3.4 - eslint: 8.57.0 - graphemer: 1.4.0 - ignore: 5.3.1 - natural-compare: 1.4.0 - semver: 7.6.0 - ts-api-utils: 1.2.1(typescript@5.3.3) - optionalDependencies: - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/eslint-plugin@7.1.0(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@eslint-community/regexpp': 4.10.0 @@ -10833,24 +10701,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3)': - dependencies: - '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 8.6.0(eslint@8.57.0)(typescript@5.3.3) - '@typescript-eslint/scope-manager': 8.6.0 - '@typescript-eslint/type-utils': 8.6.0(eslint@8.57.0)(typescript@5.3.3) - '@typescript-eslint/utils': 8.6.0(eslint@8.57.0)(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 8.6.0 - eslint: 8.57.0 - graphemer: 1.4.0 - ignore: 5.3.1 - natural-compare: 1.4.0 - ts-api-utils: 1.3.0(typescript@5.3.3) - optionalDependencies: - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@eslint-community/regexpp': 4.10.0 @@ -10887,14 +10737,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/experimental-utils@5.59.11(eslint@8.57.0)(typescript@5.3.3)': - dependencies: - '@typescript-eslint/utils': 5.59.11(eslint@8.57.0)(typescript@5.3.3) - eslint: 8.57.0 - transitivePeerDependencies: - - supports-color - - typescript - '@typescript-eslint/experimental-utils@5.59.11(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@typescript-eslint/utils': 5.59.11(eslint@8.57.0)(typescript@5.6.2) @@ -10911,19 +10753,6 @@ snapshots: - supports-color - typescript - '@typescript-eslint/parser@6.19.1(eslint@8.57.0)(typescript@5.3.3)': - dependencies: - '@typescript-eslint/scope-manager': 6.19.1 - '@typescript-eslint/types': 6.19.1 - '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 6.19.1 - debug: 4.3.4 - eslint: 8.57.0 - optionalDependencies: - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/parser@6.19.1(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@typescript-eslint/scope-manager': 6.19.1 @@ -10950,19 +10779,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3)': - dependencies: - '@typescript-eslint/scope-manager': 7.1.0 - '@typescript-eslint/types': 7.1.0 - '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 7.1.0 - debug: 4.3.4 - eslint: 8.57.0 - optionalDependencies: - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@typescript-eslint/scope-manager': 7.1.0 @@ -10976,19 +10792,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.3.3)': - dependencies: - '@typescript-eslint/scope-manager': 8.6.0 - '@typescript-eslint/types': 8.6.0 - '@typescript-eslint/typescript-estree': 8.6.0(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 8.6.0 - debug: 4.3.4 - eslint: 8.57.0 - optionalDependencies: - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@typescript-eslint/scope-manager': 8.6.0 @@ -11050,18 +10853,6 @@ snapshots: '@typescript-eslint/types': 8.6.0 '@typescript-eslint/visitor-keys': 8.6.0 - '@typescript-eslint/type-utils@6.19.1(eslint@8.57.0)(typescript@5.3.3)': - dependencies: - '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.3.3) - '@typescript-eslint/utils': 6.19.1(eslint@8.57.0)(typescript@5.3.3) - debug: 4.3.4 - eslint: 8.57.0 - ts-api-utils: 1.3.0(typescript@5.3.3) - optionalDependencies: - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/type-utils@6.19.1(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.6.2) @@ -11086,18 +10877,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@7.1.0(eslint@8.57.0)(typescript@5.3.3)': - dependencies: - '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3) - '@typescript-eslint/utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3) - debug: 4.3.4 - eslint: 8.57.0 - ts-api-utils: 1.2.1(typescript@5.3.3) - optionalDependencies: - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/type-utils@7.1.0(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.6.2) @@ -11110,18 +10889,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@7.3.1(eslint@8.57.0)(typescript@5.3.3)': - dependencies: - '@typescript-eslint/typescript-estree': 7.3.1(typescript@5.3.3) - '@typescript-eslint/utils': 7.3.1(eslint@8.57.0)(typescript@5.3.3) - debug: 4.3.4 - eslint: 8.57.0 - ts-api-utils: 1.3.0(typescript@5.3.3) - optionalDependencies: - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/type-utils@7.3.1(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@typescript-eslint/typescript-estree': 7.3.1(typescript@5.6.2) @@ -11146,18 +10913,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.6.0(eslint@8.57.0)(typescript@5.3.3)': - dependencies: - '@typescript-eslint/typescript-estree': 8.6.0(typescript@5.3.3) - '@typescript-eslint/utils': 8.6.0(eslint@8.57.0)(typescript@5.3.3) - debug: 4.3.4 - ts-api-utils: 1.3.0(typescript@5.3.3) - optionalDependencies: - typescript: 5.3.3 - transitivePeerDependencies: - - eslint - - supports-color - '@typescript-eslint/type-utils@8.6.0(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@typescript-eslint/typescript-estree': 8.6.0(typescript@5.6.2) @@ -11196,20 +10951,6 @@ snapshots: '@typescript-eslint/types@8.6.0': {} - '@typescript-eslint/typescript-estree@5.59.11(typescript@5.3.3)': - dependencies: - '@typescript-eslint/types': 5.59.11 - '@typescript-eslint/visitor-keys': 5.59.11 - debug: 4.3.4 - globby: 11.1.0 - is-glob: 4.0.3 - semver: 7.6.0 - tsutils: 3.21.0(typescript@5.3.3) - optionalDependencies: - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/typescript-estree@5.59.11(typescript@5.6.2)': dependencies: '@typescript-eslint/types': 5.59.11 @@ -11224,21 +10965,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@6.19.1(typescript@5.3.3)': - dependencies: - '@typescript-eslint/types': 6.19.1 - '@typescript-eslint/visitor-keys': 6.19.1 - debug: 4.3.4 - globby: 11.1.0 - is-glob: 4.0.3 - minimatch: 9.0.3 - semver: 7.6.0 - ts-api-utils: 1.3.0(typescript@5.3.3) - optionalDependencies: - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/typescript-estree@6.19.1(typescript@5.6.2)': dependencies: '@typescript-eslint/types': 6.19.1 @@ -11254,21 +10980,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@6.21.0(typescript@5.3.3)': - dependencies: - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/visitor-keys': 6.21.0 - debug: 4.3.4 - globby: 11.1.0 - is-glob: 4.0.3 - minimatch: 9.0.3 - semver: 7.6.0 - ts-api-utils: 1.3.0(typescript@5.3.3) - optionalDependencies: - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/typescript-estree@6.21.0(typescript@5.6.2)': dependencies: '@typescript-eslint/types': 6.21.0 @@ -11284,21 +10995,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@7.1.0(typescript@5.3.3)': - dependencies: - '@typescript-eslint/types': 7.1.0 - '@typescript-eslint/visitor-keys': 7.1.0 - debug: 4.3.4 - globby: 11.1.0 - is-glob: 4.0.3 - minimatch: 9.0.3 - semver: 7.6.0 - ts-api-utils: 1.2.1(typescript@5.3.3) - optionalDependencies: - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/typescript-estree@7.1.0(typescript@5.6.2)': dependencies: '@typescript-eslint/types': 7.1.0 @@ -11314,21 +11010,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@7.18.0(typescript@5.3.3)': - dependencies: - '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/visitor-keys': 7.18.0 - debug: 4.3.4 - globby: 11.1.0 - is-glob: 4.0.3 - minimatch: 9.0.4 - semver: 7.6.0 - ts-api-utils: 1.3.0(typescript@5.3.3) - optionalDependencies: - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/typescript-estree@7.18.0(typescript@5.6.2)': dependencies: '@typescript-eslint/types': 7.18.0 @@ -11344,21 +11025,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@7.3.1(typescript@5.3.3)': - dependencies: - '@typescript-eslint/types': 7.3.1 - '@typescript-eslint/visitor-keys': 7.3.1 - debug: 4.3.4 - globby: 11.1.0 - is-glob: 4.0.3 - minimatch: 9.0.3 - semver: 7.6.0 - ts-api-utils: 1.3.0(typescript@5.3.3) - optionalDependencies: - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/typescript-estree@7.3.1(typescript@5.6.2)': dependencies: '@typescript-eslint/types': 7.3.1 @@ -11374,21 +11040,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.6.0(typescript@5.3.3)': - dependencies: - '@typescript-eslint/types': 8.6.0 - '@typescript-eslint/visitor-keys': 8.6.0 - debug: 4.3.4 - fast-glob: 3.3.2 - is-glob: 4.0.3 - minimatch: 9.0.4 - semver: 7.6.0 - ts-api-utils: 1.3.0(typescript@5.3.3) - optionalDependencies: - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/typescript-estree@8.6.0(typescript@5.6.2)': dependencies: '@typescript-eslint/types': 8.6.0 @@ -11404,21 +11055,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@5.59.11(eslint@8.57.0)(typescript@5.3.3)': - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@types/json-schema': 7.0.15 - '@types/semver': 7.5.8 - '@typescript-eslint/scope-manager': 5.59.11 - '@typescript-eslint/types': 5.59.11 - '@typescript-eslint/typescript-estree': 5.59.11(typescript@5.3.3) - eslint: 8.57.0 - eslint-scope: 5.1.1 - semver: 7.6.0 - transitivePeerDependencies: - - supports-color - - typescript - '@typescript-eslint/utils@5.59.11(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) @@ -11449,20 +11085,6 @@ snapshots: - supports-color - typescript - '@typescript-eslint/utils@6.19.1(eslint@8.57.0)(typescript@5.3.3)': - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@types/json-schema': 7.0.15 - '@types/semver': 7.5.8 - '@typescript-eslint/scope-manager': 6.19.1 - '@typescript-eslint/types': 6.19.1 - '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.3.3) - eslint: 8.57.0 - semver: 7.6.0 - transitivePeerDependencies: - - supports-color - - typescript - '@typescript-eslint/utils@6.19.1(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) @@ -11491,20 +11113,6 @@ snapshots: - supports-color - typescript - '@typescript-eslint/utils@6.21.0(eslint@8.57.0)(typescript@5.3.3)': - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@types/json-schema': 7.0.15 - '@types/semver': 7.5.8 - '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) - eslint: 8.57.0 - semver: 7.6.0 - transitivePeerDependencies: - - supports-color - - typescript - '@typescript-eslint/utils@6.21.0(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) @@ -11533,20 +11141,6 @@ snapshots: - supports-color - typescript - '@typescript-eslint/utils@7.1.0(eslint@8.57.0)(typescript@5.3.3)': - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@types/json-schema': 7.0.15 - '@types/semver': 7.5.8 - '@typescript-eslint/scope-manager': 7.1.0 - '@typescript-eslint/types': 7.1.0 - '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3) - eslint: 8.57.0 - semver: 7.6.0 - transitivePeerDependencies: - - supports-color - - typescript - '@typescript-eslint/utils@7.1.0(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) @@ -11561,17 +11155,6 @@ snapshots: - supports-color - typescript - '@typescript-eslint/utils@7.18.0(eslint@8.57.0)(typescript@5.3.3)': - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@typescript-eslint/scope-manager': 7.18.0 - '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.3.3) - eslint: 8.57.0 - transitivePeerDependencies: - - supports-color - - typescript - '@typescript-eslint/utils@7.18.0(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) @@ -11594,20 +11177,6 @@ snapshots: - supports-color - typescript - '@typescript-eslint/utils@7.3.1(eslint@8.57.0)(typescript@5.3.3)': - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@types/json-schema': 7.0.15 - '@types/semver': 7.5.8 - '@typescript-eslint/scope-manager': 7.3.1 - '@typescript-eslint/types': 7.3.1 - '@typescript-eslint/typescript-estree': 7.3.1(typescript@5.3.3) - eslint: 8.57.0 - semver: 7.6.0 - transitivePeerDependencies: - - supports-color - - typescript - '@typescript-eslint/utils@7.3.1(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) @@ -11636,17 +11205,6 @@ snapshots: - supports-color - typescript - '@typescript-eslint/utils@8.6.0(eslint@8.57.0)(typescript@5.3.3)': - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@typescript-eslint/scope-manager': 8.6.0 - '@typescript-eslint/types': 8.6.0 - '@typescript-eslint/typescript-estree': 8.6.0(typescript@5.3.3) - eslint: 8.57.0 - transitivePeerDependencies: - - supports-color - - typescript - '@typescript-eslint/utils@8.6.0(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) @@ -12455,15 +12013,6 @@ snapshots: dependencies: layout-base: 1.0.2 - cosmiconfig@8.3.6(typescript@5.3.3): - dependencies: - import-fresh: 3.3.0 - js-yaml: 4.1.0 - parse-json: 5.2.0 - path-type: 4.0.0 - optionalDependencies: - typescript: 5.3.3 - cosmiconfig@8.3.6(typescript@5.6.2): dependencies: import-fresh: 3.3.0 @@ -13111,13 +12660,6 @@ snapshots: eslint-plugin-react: 7.34.1(eslint@8.57.1) eslint-plugin-react-hooks: 4.6.0(eslint@8.57.1) - eslint-config-xo-typescript@1.0.1(@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3))(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3): - dependencies: - '@typescript-eslint/eslint-plugin': 8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3) - '@typescript-eslint/parser': 8.6.0(eslint@8.57.0)(typescript@5.3.3) - eslint: 8.57.0 - typescript: 5.3.3 - eslint-config-xo-typescript@1.0.1(@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2))(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2): dependencies: '@typescript-eslint/eslint-plugin': 8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2) @@ -13206,20 +12748,6 @@ snapshots: - eslint - supports-color - eslint-plugin-functional@6.1.1(eslint@8.57.0)(typescript@5.3.3): - dependencies: - '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.3.3) - deepmerge-ts: 5.1.0 - escape-string-regexp: 4.0.0 - eslint: 8.57.0 - is-immutable-type: 2.0.2(eslint@8.57.0)(typescript@5.3.3) - semver: 7.6.0 - ts-api-utils: 1.3.0(typescript@5.3.3) - optionalDependencies: - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - eslint-plugin-functional@6.1.1(eslint@8.57.0)(typescript@5.6.2): dependencies: '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.6.2) @@ -13248,23 +12776,6 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import-x@3.1.0(eslint@8.57.0)(typescript@5.3.3): - dependencies: - '@typescript-eslint/utils': 7.18.0(eslint@8.57.0)(typescript@5.3.3) - debug: 4.3.4 - doctrine: 3.0.0 - eslint: 8.57.0 - eslint-import-resolver-node: 0.3.9 - get-tsconfig: 4.8.0 - is-glob: 4.0.3 - minimatch: 9.0.4 - semver: 7.6.0 - stable-hash: 0.0.4 - tslib: 2.6.2 - transitivePeerDependencies: - - supports-color - - typescript - eslint-plugin-import-x@3.1.0(eslint@8.57.0)(typescript@5.6.2): dependencies: '@typescript-eslint/utils': 7.18.0(eslint@8.57.0)(typescript@5.6.2) @@ -13353,82 +12864,6 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-mmkal@0.10.1(eslint@8.57.0)(typescript@5.3.3)(vitest@1.2.2(@types/node@20.11.17)(sass@1.71.0)): - dependencies: - '@eslint/js': 8.57.1 - '@next/eslint-plugin-next': 14.2.3 - '@rushstack/eslint-config': 3.6.5(eslint@8.57.0)(typescript@5.3.3) - '@rushstack/eslint-plugin': 0.13.1(eslint@8.57.0)(typescript@5.3.3) - '@rushstack/eslint-plugin-packlets': 0.8.1(eslint@8.57.0)(typescript@5.3.3) - '@rushstack/eslint-plugin-security': 0.7.1(eslint@8.57.0)(typescript@5.3.3) - '@types/eslint': 8.56.6 - '@typescript-eslint/eslint-plugin': 8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3) - '@typescript-eslint/parser': 8.6.0(eslint@8.57.0)(typescript@5.3.3) - eslint-config-prettier: 9.1.0(eslint@8.57.0) - eslint-config-xo: 0.43.1(eslint@8.57.0) - eslint-config-xo-react: 0.27.0(eslint-plugin-react-hooks@4.6.0(eslint@8.57.0))(eslint-plugin-react@7.34.1(eslint@8.57.0))(eslint@8.57.0) - eslint-config-xo-typescript: 1.0.1(@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3))(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3) - eslint-plugin-codegen: 0.29.0(eslint@8.57.0) - eslint-plugin-functional: 6.1.1(eslint@8.57.0)(typescript@5.3.3) - eslint-plugin-import-x: 3.1.0(eslint@8.57.0)(typescript@5.3.3) - eslint-plugin-jsx-a11y: 6.8.0(eslint@8.57.0) - eslint-plugin-markdown: 4.0.1(eslint@8.57.0) - eslint-plugin-prettier: 5.1.3(@types/eslint@8.56.6)(eslint-config-prettier@9.1.0(eslint@8.57.0))(eslint@8.57.0)(prettier@3.2.5) - eslint-plugin-promise: 6.1.1(eslint@8.57.0) - eslint-plugin-react: 7.34.1(eslint@8.57.0) - eslint-plugin-react-hooks: 4.6.0(eslint@8.57.0) - eslint-plugin-unicorn: 49.0.0(eslint@8.57.0) - eslint-plugin-vitest: 0.3.26(@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3)(vitest@1.2.2(@types/node@20.11.17)(sass@1.71.0)) - eslint-plugin-wrapper: 0.1.0-1 - globals: 14.0.0 - lodash: 4.17.21 - prettier: 3.2.5 - typescript-eslint: 8.6.0(eslint@8.57.0)(typescript@5.3.3) - transitivePeerDependencies: - - babel-plugin-macros - - eslint - - supports-color - - typescript - - vitest - - eslint-plugin-mmkal@0.10.1(eslint@8.57.0)(typescript@5.3.3)(vitest@1.2.2(@types/node@20.12.12)(sass@1.71.0)): - dependencies: - '@eslint/js': 8.57.1 - '@next/eslint-plugin-next': 14.2.3 - '@rushstack/eslint-config': 3.6.5(eslint@8.57.0)(typescript@5.3.3) - '@rushstack/eslint-plugin': 0.13.1(eslint@8.57.0)(typescript@5.3.3) - '@rushstack/eslint-plugin-packlets': 0.8.1(eslint@8.57.0)(typescript@5.3.3) - '@rushstack/eslint-plugin-security': 0.7.1(eslint@8.57.0)(typescript@5.3.3) - '@types/eslint': 8.56.6 - '@typescript-eslint/eslint-plugin': 8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3) - '@typescript-eslint/parser': 8.6.0(eslint@8.57.0)(typescript@5.3.3) - eslint-config-prettier: 9.1.0(eslint@8.57.0) - eslint-config-xo: 0.43.1(eslint@8.57.0) - eslint-config-xo-react: 0.27.0(eslint-plugin-react-hooks@4.6.0(eslint@8.57.0))(eslint-plugin-react@7.34.1(eslint@8.57.0))(eslint@8.57.0) - eslint-config-xo-typescript: 1.0.1(@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3))(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3) - eslint-plugin-codegen: 0.29.0(eslint@8.57.0) - eslint-plugin-functional: 6.1.1(eslint@8.57.0)(typescript@5.3.3) - eslint-plugin-import-x: 3.1.0(eslint@8.57.0)(typescript@5.3.3) - eslint-plugin-jsx-a11y: 6.8.0(eslint@8.57.0) - eslint-plugin-markdown: 4.0.1(eslint@8.57.0) - eslint-plugin-prettier: 5.1.3(@types/eslint@8.56.6)(eslint-config-prettier@9.1.0(eslint@8.57.0))(eslint@8.57.0)(prettier@3.2.5) - eslint-plugin-promise: 6.1.1(eslint@8.57.0) - eslint-plugin-react: 7.34.1(eslint@8.57.0) - eslint-plugin-react-hooks: 4.6.0(eslint@8.57.0) - eslint-plugin-unicorn: 49.0.0(eslint@8.57.0) - eslint-plugin-vitest: 0.3.26(@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3)(vitest@1.2.2(@types/node@20.12.12)(sass@1.71.0)) - eslint-plugin-wrapper: 0.1.0-1 - globals: 14.0.0 - lodash: 4.17.21 - prettier: 3.2.5 - typescript-eslint: 8.6.0(eslint@8.57.0)(typescript@5.3.3) - transitivePeerDependencies: - - babel-plugin-macros - - eslint - - supports-color - - typescript - - vitest - eslint-plugin-mmkal@0.10.1(eslint@8.57.0)(typescript@5.6.2)(vitest@1.2.2(@types/node@20.11.17)(sass@1.71.0)): dependencies: '@eslint/js': 8.57.1 @@ -13739,11 +13174,11 @@ snapshots: semver: 6.3.1 string.prototype.matchall: 4.0.11 - eslint-plugin-tailwindcss@3.15.1(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.11.17)(typescript@5.3.3))): + eslint-plugin-tailwindcss@3.15.1(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.11.17)(typescript@5.6.2))): dependencies: fast-glob: 3.3.2 postcss: 8.4.38 - tailwindcss: 3.4.3(ts-node@10.9.2(@types/node@20.11.17)(typescript@5.3.3)) + tailwindcss: 3.4.3(ts-node@10.9.2(@types/node@20.11.17)(typescript@5.6.2)) eslint-plugin-tsdoc@0.2.17: dependencies: @@ -13786,28 +13221,6 @@ snapshots: semver: 7.6.0 strip-indent: 3.0.0 - eslint-plugin-vitest@0.3.26(@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3)(vitest@1.2.2(@types/node@20.11.17)(sass@1.71.0)): - dependencies: - '@typescript-eslint/utils': 7.18.0(eslint@8.57.0)(typescript@5.3.3) - eslint: 8.57.0 - optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3) - vitest: 1.2.2(@types/node@20.11.17)(sass@1.71.0) - transitivePeerDependencies: - - supports-color - - typescript - - eslint-plugin-vitest@0.3.26(@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3)(vitest@1.2.2(@types/node@20.12.12)(sass@1.71.0)): - dependencies: - '@typescript-eslint/utils': 7.18.0(eslint@8.57.0)(typescript@5.3.3) - eslint: 8.57.0 - optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3) - vitest: 1.2.2(@types/node@20.12.12)(sass@1.71.0) - transitivePeerDependencies: - - supports-color - - typescript - eslint-plugin-vitest@0.3.26(@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2)(vitest@1.2.2(@types/node@20.11.17)(sass@1.71.0)): dependencies: '@typescript-eslint/utils': 7.18.0(eslint@8.57.0)(typescript@5.6.2) @@ -14916,15 +14329,6 @@ snapshots: is-hexadecimal@2.0.1: {} - is-immutable-type@2.0.2(eslint@8.57.0)(typescript@5.3.3): - dependencies: - '@typescript-eslint/type-utils': 7.3.1(eslint@8.57.0)(typescript@5.3.3) - eslint: 8.57.0 - ts-api-utils: 1.3.0(typescript@5.3.3) - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - is-immutable-type@2.0.2(eslint@8.57.0)(typescript@5.6.2): dependencies: '@typescript-eslint/type-utils': 7.3.1(eslint@8.57.0)(typescript@5.6.2) @@ -16778,14 +16182,6 @@ snapshots: camelcase-css: 2.0.1 postcss: 8.4.38 - postcss-load-config@4.0.2(postcss@8.4.38)(ts-node@10.9.2(@types/node@20.11.17)(typescript@5.3.3)): - dependencies: - lilconfig: 3.0.0 - yaml: 2.3.4 - optionalDependencies: - postcss: 8.4.38 - ts-node: 10.9.2(@types/node@20.11.17)(typescript@5.3.3) - postcss-load-config@4.0.2(postcss@8.4.38)(ts-node@10.9.2(@types/node@20.11.17)(typescript@5.6.2)): dependencies: lilconfig: 3.0.0 @@ -17452,7 +16848,7 @@ snapshots: setprototypeof@1.2.0: {} - shadcn-ui@0.8.0(typescript@5.3.3): + shadcn-ui@0.8.0(typescript@5.6.2): dependencies: '@antfu/ni': 0.21.12 '@babel/core': 7.23.9 @@ -17460,7 +16856,7 @@ snapshots: '@babel/plugin-transform-typescript': 7.24.4(@babel/core@7.23.9) chalk: 5.2.0 commander: 10.0.1 - cosmiconfig: 8.3.6(typescript@5.3.3) + cosmiconfig: 8.3.6(typescript@5.6.2) diff: 5.2.0 execa: 7.2.0 fast-glob: 3.3.2 @@ -17874,11 +17270,11 @@ snapshots: dependencies: '@babel/runtime': 7.24.4 - tailwindcss-animate@1.0.7(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.11.17)(typescript@5.3.3))): + tailwindcss-animate@1.0.7(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.11.17)(typescript@5.6.2))): dependencies: - tailwindcss: 3.4.3(ts-node@10.9.2(@types/node@20.11.17)(typescript@5.3.3)) + tailwindcss: 3.4.3(ts-node@10.9.2(@types/node@20.11.17)(typescript@5.6.2)) - tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.11.17)(typescript@5.3.3)): + tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.11.17)(typescript@5.6.2)): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -17897,7 +17293,7 @@ snapshots: postcss: 8.4.38 postcss-import: 15.1.0(postcss@8.4.38) postcss-js: 4.0.1(postcss@8.4.38) - postcss-load-config: 4.0.2(postcss@8.4.38)(ts-node@10.9.2(@types/node@20.11.17)(typescript@5.3.3)) + postcss-load-config: 4.0.2(postcss@8.4.38)(ts-node@10.9.2(@types/node@20.11.17)(typescript@5.6.2)) postcss-nested: 6.0.1(postcss@8.4.38) postcss-selector-parser: 6.0.16 resolve: 1.22.8 @@ -17993,25 +17389,7 @@ snapshots: trough@2.2.0: {} - trpc-cli@0.2.1(@trpc/server@10.45.2)(zod@3.23.8): - dependencies: - '@trpc/server': 10.45.2 - cleye: 1.3.2 - picocolors: 1.0.1 - zod: 3.23.8 - zod-to-json-schema: 3.23.0(zod@3.23.8) - zod-validation-error: 3.3.0(zod@3.23.8) - - trpc-cli@0.3.0(@trpc/server@10.45.2)(zod@3.23.8): - dependencies: - '@trpc/server': 10.45.2 - cleye: 1.3.2 - picocolors: 1.0.1 - zod: 3.23.8 - zod-to-json-schema: 3.23.0(zod@3.23.8) - zod-validation-error: 3.3.0(zod@3.23.8) - - trpc-cli@https://pkg.pr.new/mmkal/trpc-cli@38(@trpc/server@10.45.2)(zod@3.23.8): + trpc-cli@0.5.0(@trpc/server@10.45.2)(zod@3.23.8): dependencies: '@trpc/server': 10.45.2 cleye: 1.3.2 @@ -18020,18 +17398,10 @@ snapshots: zod-to-json-schema: 3.23.0(zod@3.23.8) zod-validation-error: 3.3.0(zod@3.23.8) - ts-api-utils@1.2.1(typescript@5.3.3): - dependencies: - typescript: 5.3.3 - ts-api-utils@1.2.1(typescript@5.6.2): dependencies: typescript: 5.6.2 - ts-api-utils@1.3.0(typescript@5.3.3): - dependencies: - typescript: 5.3.3 - ts-api-utils@1.3.0(typescript@5.6.2): dependencies: typescript: 5.6.2 @@ -18052,25 +17422,6 @@ snapshots: '@ts-morph/common': 0.22.0 code-block-writer: 12.0.0 - ts-node@10.9.2(@types/node@20.11.17)(typescript@5.3.3): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.9 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 20.11.17 - acorn: 8.11.3 - acorn-walk: 8.3.2 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.3.3 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - optional: true - ts-node@10.9.2(@types/node@20.11.17)(typescript@5.6.2): dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -18158,11 +17509,6 @@ snapshots: - supports-color - ts-node - tsutils@3.21.0(typescript@5.3.3): - dependencies: - tslib: 1.14.1 - typescript: 5.3.3 - tsutils@3.21.0(typescript@5.6.2): dependencies: tslib: 1.14.1 @@ -18175,32 +17521,32 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - turbo-darwin-64@1.12.3: + turbo-darwin-64@2.1.2: optional: true - turbo-darwin-arm64@1.12.3: + turbo-darwin-arm64@2.1.2: optional: true - turbo-linux-64@1.12.3: + turbo-linux-64@2.1.2: optional: true - turbo-linux-arm64@1.12.3: + turbo-linux-arm64@2.1.2: optional: true - turbo-windows-64@1.12.3: + turbo-windows-64@2.1.2: optional: true - turbo-windows-arm64@1.12.3: + turbo-windows-arm64@2.1.2: optional: true - turbo@1.12.3: + turbo@2.1.2: optionalDependencies: - turbo-darwin-64: 1.12.3 - turbo-darwin-arm64: 1.12.3 - turbo-linux-64: 1.12.3 - turbo-linux-arm64: 1.12.3 - turbo-windows-64: 1.12.3 - turbo-windows-arm64: 1.12.3 + turbo-darwin-64: 2.1.2 + turbo-darwin-arm64: 2.1.2 + turbo-linux-64: 2.1.2 + turbo-linux-arm64: 2.1.2 + turbo-windows-64: 2.1.2 + turbo-windows-arm64: 2.1.2 type-check@0.4.0: dependencies: @@ -18288,16 +17634,6 @@ snapshots: types-eslintrc: 1.0.3 types-json: 1.2.2 - typescript-eslint@7.1.0(eslint@8.57.0)(typescript@5.3.3): - dependencies: - '@typescript-eslint/eslint-plugin': 7.1.0(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3) - '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3) - eslint: 8.57.0 - optionalDependencies: - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color - typescript-eslint@7.1.0(eslint@8.57.0)(typescript@5.6.2): dependencies: '@typescript-eslint/eslint-plugin': 7.1.0(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2) @@ -18308,17 +17644,6 @@ snapshots: transitivePeerDependencies: - supports-color - typescript-eslint@8.6.0(eslint@8.57.0)(typescript@5.3.3): - dependencies: - '@typescript-eslint/eslint-plugin': 8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3) - '@typescript-eslint/parser': 8.6.0(eslint@8.57.0)(typescript@5.3.3) - '@typescript-eslint/utils': 8.6.0(eslint@8.57.0)(typescript@5.3.3) - optionalDependencies: - typescript: 5.3.3 - transitivePeerDependencies: - - eslint - - supports-color - typescript-eslint@8.6.0(eslint@8.57.0)(typescript@5.6.2): dependencies: '@typescript-eslint/eslint-plugin': 8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2) @@ -18341,8 +17666,6 @@ snapshots: - eslint - supports-color - typescript@5.3.3: {} - typescript@5.6.2: {} ufo@1.4.0: {} diff --git a/tools/npmono/package.json b/tools/npmono/package.json index b2dc4aab..1ccadadf 100644 --- a/tools/npmono/package.json +++ b/tools/npmono/package.json @@ -12,7 +12,7 @@ "listr2": "^8.0.2", "semver": "^7.6.0", "sort-package-json": "^2.8.0", - "trpc-cli": "https://pkg.pr.new/mmkal/trpc-cli@38" + "trpc-cli": "^0.5.0" }, "devDependencies": { "@types/semver": "^7.5.8", diff --git a/turbo.json b/turbo.json index 2df92059..0a6e5f6f 100644 --- a/turbo.json +++ b/turbo.json @@ -1,12 +1,12 @@ { "$schema": "https://turbo.build/schema.json", - "pipeline": { + "tasks": { "build": { "dependsOn": ["^build"], "outputs": ["dist"] }, "test": { - "dependsOn": ["^build", "^test"] + "dependsOn": ["^build"] } } }