diff --git a/integration-tests/testkit/seed.ts b/integration-tests/testkit/seed.ts index f98297285..71821303a 100644 --- a/integration-tests/testkit/seed.ts +++ b/integration-tests/testkit/seed.ts @@ -353,7 +353,7 @@ export function initSeed() { const result = await pool.any(psql` UPDATE "organization_access_tokens" SET "expires_at" = NOW() - WHERE id IN (${psql.join(tokenIds, psql`, `)}) AND organization_id = ${organization.id} + WHERE id IN (${psql.join(tokenIds, psql.fragment`, `)}) AND organization_id = ${organization.id} RETURNING "id" `); diff --git a/integration-tests/tests/api/organization/members.spec.ts b/integration-tests/tests/api/organization/members.spec.ts index c1e6c7d56..c3e0ff928 100644 --- a/integration-tests/tests/api/organization/members.spec.ts +++ b/integration-tests/tests/api/organization/members.spec.ts @@ -1,3 +1,4 @@ +import { pollFor } from 'testkit/flow'; import { graphql } from 'testkit/gql'; import { ResourceAssignmentModeType } from 'testkit/gql/graphql'; import { execute } from 'testkit/graphql'; @@ -115,15 +116,19 @@ test.concurrent('cannot delete a role with members', async ({ expect }) => { test.concurrent('email invitation', async ({ expect }) => { const seed = initSeed(); const { createOrg } = await seed.createOwner(); - const { inviteMember } = await createOrg(); + const { inviteMember, organization } = await createOrg(); const inviteEmail = seed.generateEmail(); const invitationResult = await inviteMember(inviteEmail); const inviteCode = invitationResult.ok?.createdOrganizationInvitation.code; expect(inviteCode).toBeDefined(); - const sentEmails = await history(); - expect(sentEmails).toContainEqual(expect.objectContaining({ to: inviteEmail })); + await pollFor(async () => { + const sentEmails = await history(inviteEmail); + return sentEmails.length > 0; + }); + const sentEmails = await history(inviteEmail); + expect(sentEmails[0].subject).toEqual('You have been invited to join ' + organization.slug); }); test.concurrent('can not invite with role not existing in organization', async ({ expect }) => { diff --git a/package.json b/package.json index 049bab3f7..f26139f07 100644 --- a/package.json +++ b/package.json @@ -175,7 +175,6 @@ "eslint@8.57.1": "patches/eslint@8.57.1.patch", "@graphql-eslint/eslint-plugin@3.20.1": "patches/@graphql-eslint__eslint-plugin@3.20.1.patch", "got@14.4.7": "patches/got@14.4.7.patch", - "slonik@30.4.4": "patches/slonik@30.4.4.patch", "@oclif/core@3.26.6": "patches/@oclif__core@3.26.6.patch", "oclif": "patches/oclif.patch", "graphiql": "patches/graphiql.patch", @@ -184,7 +183,9 @@ "p-cancelable@4.0.1": "patches/p-cancelable@4.0.1.patch", "bentocache": "patches/bentocache.patch", "@graphql-codegen/schema-ast": "patches/@graphql-codegen__schema-ast.patch", - "@fastify/vite": "patches/@fastify__vite.patch" + "@fastify/vite": "patches/@fastify__vite.patch", + "@slonik/pg-driver": "patches/@slonik__pg-driver.patch", + "slonik": "patches/slonik.patch" }, "onlyBuiltDependencies": [ "msw" diff --git a/packages/internal/postgres/package.json b/packages/internal/postgres/package.json index 53ea3a791..caa71fecf 100644 --- a/packages/internal/postgres/package.json +++ b/packages/internal/postgres/package.json @@ -7,8 +7,11 @@ ".": "./src/index.ts" }, "dependencies": { - "slonik": "30.4.4", - "slonik-interceptor-query-logging": "46.4.0" + "@standard-schema/spec": "1.0.0", + "slonik": "48.13.2", + "slonik-interceptor-query-logging": "48.13.2", + "slonik-sql-tag-raw": "48.13.2", + "zod": "3.25.76" }, "devDependencies": { "@hive/service-common": "workspace:*" diff --git a/packages/internal/postgres/src/index.ts b/packages/internal/postgres/src/index.ts index 78058a1cb..b41011252 100644 --- a/packages/internal/postgres/src/index.ts +++ b/packages/internal/postgres/src/index.ts @@ -4,11 +4,10 @@ export { type CommonQueryMethods, } from './postgres-database-pool'; export { type PostgresConnectionParamaters, createConnectionString } from './connection-string'; -export { psql } from './psql'; +export { psql, type TaggedTemplateLiteralInvocation } from './psql'; export { UniqueIntegrityConstraintViolationError, ForeignKeyIntegrityConstraintViolationError, - type TaggedTemplateLiteralInvocation, type PrimitiveValueExpression, type SerializableValue, type Interceptor, diff --git a/packages/internal/postgres/src/pg-pool-bridge.ts b/packages/internal/postgres/src/pg-pool-bridge.ts new file mode 100644 index 000000000..2bbd8016d --- /dev/null +++ b/packages/internal/postgres/src/pg-pool-bridge.ts @@ -0,0 +1,91 @@ +import type { PoolClient } from 'pg'; +import { sql, type DatabasePool, type DatabasePoolConnection } from 'slonik'; +import { raw } from 'slonik-sql-tag-raw'; + +/** + * Bridge {slonik.DatabasePool} to an {pg.Pool} for usage with Postgraphile Workers. + * + * This is a very very pragmatic approach, since slonik moved away from using {pg.Pool} internally. + * https://github.com/gajus/slonik/issues/768 + **/ +export class PgPoolBridge { + constructor(private pool: DatabasePool) {} + + end(): never { + throw new Error('Not implemented.'); + } + + async connect(): Promise { + const pgClientAvailableP = Promise.withResolvers(); + const pgClientReleasedP = Promise.withResolvers(); + + // slonik connect works in a way where the client is acquired for the callback handler closure only. + // It is released once the Promise returned from the handler resolves. + // We need to be a bit creative to support the "pg.Pool" API and obviously + // trust graphile-workers to call the `release` method on our fake {pg.Client} :) + // so the client is released back to the pool + void this.pool.connect(async client => { + pgClientAvailableP.resolve(new PgClientBridge(client, pgClientReleasedP.resolve)); + await pgClientReleasedP.promise; + }); + + return pgClientAvailableP.promise; + } + + /** Some of graphile-workers logic just calls the `query` method on {pg.Pool} - without first acquiring a connection. */ + query(query: unknown, values?: unknown, callback?: unknown): any { + // not used, but just in case so we can catch it in the future... + if (typeof callback !== 'undefined') { + throw new Error('PgClientBridge.query: callback not supported'); + } + + if ((typeof query !== 'string' && typeof query !== 'object') || !query) { + throw new Error('PgClientBridge.query: unsupported query input'); + } + + if (typeof query === 'string') { + return this.pool.query(sql.unsafe`${raw(query, values as any)}`); + } + + return this.pool.query(sql.unsafe`${raw((query as any).text as any, (query as any).values)}`); + } + + on(): this { + // Note: we can skip setting up event handlers, as graphile workers is only setting up error handlers to avoid uncaught exceptions + // For us, the error handlers are already set up by slonik + // https://github.com/graphile/worker/blob/5650fbc4406fa3ce197b2ab582e08fd20974e50c/src/lib.ts#L351-L359 + return this; + } + + removeListener(): this { + // Note: we can skip tearing down event handlers, since we ship setting them up in the first place + // https://github.com/graphile/worker/blob/5650fbc4406fa3ce197b2ab582e08fd20974e50c/src/lib.ts#L351-L359 + return this; + } +} + +class PgClientBridge { + constructor( + private connection: DatabasePoolConnection, + /** This is invoked for again releasing the connection. */ + public release: () => void, + ) {} + + query(query: unknown, values?: unknown, callback?: unknown): any { + if (typeof callback !== 'undefined') { + throw new Error('PgClientBridge.query: callback not supported'); + } + + if ((typeof query !== 'string' && typeof query !== 'object') || !query) { + throw new Error('PgClientBridge.query: unsupported query input'); + } + + if (typeof query === 'string') { + return this.connection.query(sql.unsafe`${raw(query, values as any)}`); + } + + return this.connection.query( + sql.unsafe`${raw((query as any).text as any, (query as any).values)}`, + ); + } +} diff --git a/packages/internal/postgres/src/postgres-database-pool.ts b/packages/internal/postgres/src/postgres-database-pool.ts index 4d863de43..8098abda8 100644 --- a/packages/internal/postgres/src/postgres-database-pool.ts +++ b/packages/internal/postgres/src/postgres-database-pool.ts @@ -1,54 +1,58 @@ +import type { Pool } from 'pg'; import { createPool, + createTypeParserPreset, type DatabasePool, type Interceptor, type PrimitiveValueExpression, - type QueryResultRow, - type QueryResultRowColumn, + type QuerySqlToken, type CommonQueryMethods as SlonikCommonQueryMethods, - type TaggedTemplateLiteralInvocation, } from 'slonik'; import { createQueryLoggingInterceptor } from 'slonik-interceptor-query-logging'; import { context, SpanKind, SpanStatusCode, trace } from '@hive/service-common'; +import type { StandardSchemaV1 } from '@standard-schema/spec'; import { createConnectionString, type PostgresConnectionParamaters } from './connection-string'; +import { PgPoolBridge } from './pg-pool-bridge'; const tracer = trace.getTracer('storage'); export interface CommonQueryMethods { - exists( - sql: TaggedTemplateLiteralInvocation, + exists( + sql: QuerySqlToken, values?: PrimitiveValueExpression[], ): Promise; - any( - sql: TaggedTemplateLiteralInvocation, + any( + sql: QuerySqlToken, values?: PrimitiveValueExpression[], - ): Promise>; - maybeOne( - sql: TaggedTemplateLiteralInvocation, + ): Promise>>; + maybeOne( + sql: QuerySqlToken, values?: PrimitiveValueExpression[], - ): Promise; - query(sql: TaggedTemplateLiteralInvocation, values?: PrimitiveValueExpression[]): Promise; - oneFirst( - sql: TaggedTemplateLiteralInvocation, + ): Promise>; + query(sql: QuerySqlToken, values?: PrimitiveValueExpression[]): Promise; + oneFirst( + sql: QuerySqlToken, values?: PrimitiveValueExpression[], - ): Promise; - one(sql: TaggedTemplateLiteralInvocation, values?: PrimitiveValueExpression[]): Promise; - anyFirst( - sql: TaggedTemplateLiteralInvocation, + ): Promise[keyof StandardSchemaV1.InferOutput]>; + one( + sql: QuerySqlToken, values?: PrimitiveValueExpression[], - ): Promise>; - maybeOneFirst( - sql: TaggedTemplateLiteralInvocation, + ): Promise>; + anyFirst( + sql: QuerySqlToken, values?: PrimitiveValueExpression[], - ): Promise; + ): Promise[keyof StandardSchemaV1.InferOutput]>>; + maybeOneFirst( + sql: QuerySqlToken, + values?: PrimitiveValueExpression[], + ): Promise[keyof StandardSchemaV1.InferOutput]>; } export class PostgresDatabasePool implements CommonQueryMethods { constructor(private pool: DatabasePool) {} - /** Retrieve the raw PgPool instance. Refrain from using this API. It only exists for postgraphile workers */ - getRawPgPool() { - return this.pool.pool; + getPgPoolCompat(): Pool { + return new PgPoolBridge(this.pool) as any; } /** Retrieve the raw Slonik instance. Refrain from using this API. */ @@ -56,59 +60,58 @@ export class PostgresDatabasePool implements CommonQueryMethods { return this.pool; } - async exists( - sql: TaggedTemplateLiteralInvocation, + async exists( + sql: QuerySqlToken, values?: PrimitiveValueExpression[], ): Promise { return this.pool.exists(sql, values); } - async any( - sql: TaggedTemplateLiteralInvocation, + async any( + sql: QuerySqlToken, values?: PrimitiveValueExpression[], - ): Promise> { - return this.pool.any(sql, values); + ): Promise>> { + return this.pool.any(sql, values); } - async maybeOne( - sql: TaggedTemplateLiteralInvocation, + async maybeOne( + sql: QuerySqlToken, values?: PrimitiveValueExpression[], - ): Promise { + ): Promise> { return this.pool.maybeOne(sql, values); } - async query( - sql: TaggedTemplateLiteralInvocation, - values?: PrimitiveValueExpression[], - ): Promise { - await this.pool.query(sql, values); + async query(sql: QuerySqlToken, values?: PrimitiveValueExpression[]): Promise { + await this.pool.query(sql, values); } - async oneFirst( - sql: TaggedTemplateLiteralInvocation, + async oneFirst( + sql: QuerySqlToken, values?: PrimitiveValueExpression[], - ): Promise { + ): Promise[keyof StandardSchemaV1.InferOutput]> { return await this.pool.oneFirst(sql, values); } - async maybeOneFirst( - sql: TaggedTemplateLiteralInvocation, + async maybeOneFirst( + sql: QuerySqlToken, values?: PrimitiveValueExpression[], - ): Promise { + ): Promise[keyof StandardSchemaV1.InferOutput]> { return await this.pool.maybeOneFirst(sql, values); } - async one( - sql: TaggedTemplateLiteralInvocation, + async one( + sql: QuerySqlToken, values?: PrimitiveValueExpression[], - ): Promise { + ): Promise> { return await this.pool.one(sql, values); } - async anyFirst( - sql: TaggedTemplateLiteralInvocation, + async anyFirst( + sql: QuerySqlToken, values?: PrimitiveValueExpression[], - ): Promise> { + ): Promise< + ReadonlyArray[keyof StandardSchemaV1.InferOutput]> + > { return await this.pool.anyFirst(sql, values); } @@ -124,54 +127,19 @@ export class PostgresDatabasePool implements CommonQueryMethods { return await this.pool.transaction(async methods => { try { return await handler({ - async exists( - sql: TaggedTemplateLiteralInvocation, - values?: PrimitiveValueExpression[], - ): Promise { - return methods.exists(sql, values); - }, - async any( - sql: TaggedTemplateLiteralInvocation, - values?: PrimitiveValueExpression[], - ): Promise> { - return methods.any(sql, values); - }, - async maybeOne( - sql: TaggedTemplateLiteralInvocation, - values?: PrimitiveValueExpression[], - ): Promise { - return methods.maybeOne(sql, values); - }, + exists: methods.exists, + any: methods.any, + maybeOne: methods.maybeOne, async query( - sql: TaggedTemplateLiteralInvocation, + sql: QuerySqlToken, values?: PrimitiveValueExpression[], ): Promise { - await methods.query(sql, values); - }, - async oneFirst( - sql: TaggedTemplateLiteralInvocation, - values?: PrimitiveValueExpression[], - ): Promise { - return await methods.oneFirst(sql, values); - }, - async maybeOneFirst( - sql: TaggedTemplateLiteralInvocation, - values?: PrimitiveValueExpression[], - ): Promise { - return await methods.maybeOneFirst(sql, values); - }, - async anyFirst( - sql: TaggedTemplateLiteralInvocation, - values?: PrimitiveValueExpression[], - ): Promise> { - return await methods.anyFirst(sql, values); - }, - async one( - sql: TaggedTemplateLiteralInvocation, - values?: PrimitiveValueExpression[], - ): Promise { - return await methods.one(sql, values); + await methods.query(sql, values); }, + oneFirst: methods.oneFirst, + maybeOneFirst: methods.maybeOneFirst, + anyFirst: methods.anyFirst, + one: methods.one, }); } catch (err) { span.setAttribute('error', 'true'); @@ -200,6 +168,14 @@ export class PostgresDatabasePool implements CommonQueryMethods { const dbInterceptors: Interceptor[] = [createQueryLoggingInterceptor()]; +const typeParsers = [ + ...createTypeParserPreset().filter(parser => parser.name !== 'int8'), + { + name: 'int8', + parse: (value: string) => parseInt(value, 10), + }, +]; + export async function createPostgresDatabasePool(args: { connectionParameters: PostgresConnectionParamaters | string; maximumPoolSize?: number; @@ -212,6 +188,7 @@ export async function createPostgresDatabasePool(args: { : createConnectionString(args.connectionParameters); const pool = await createPool(connectionString, { interceptors: dbInterceptors.concat(args.additionalInterceptors ?? []), + typeParsers, captureStackTrace: false, maximumPoolSize: args.maximumPoolSize, idleTimeout: 30000, @@ -223,10 +200,10 @@ export async function createPostgresDatabasePool(args: { ) { const original: SlonikCommonQueryMethods[K] = pool[methodName]; - function interceptor( + function interceptor( this: any, - sql: TaggedTemplateLiteralInvocation, - values?: QueryResultRowColumn[], + sql: QuerySqlToken, + values?: PrimitiveValueExpression[], ): any { return (original as any).call(this, sql, values).catch((error: any) => { error.sql = sql.sql; @@ -236,7 +213,7 @@ export async function createPostgresDatabasePool(args: { }); } - pool[methodName] = interceptor; + pool[methodName] = interceptor as any; } interceptError('one'); diff --git a/packages/internal/postgres/src/psql.ts b/packages/internal/postgres/src/psql.ts index 5342c5c08..04893f345 100644 --- a/packages/internal/postgres/src/psql.ts +++ b/packages/internal/postgres/src/psql.ts @@ -1,3 +1,23 @@ -import { createSqlTag } from 'slonik'; +import { createSqlTag, QuerySqlToken, type SqlTag, type ValueExpression } from 'slonik'; +import { z } from 'zod'; +import { StandardSchemaV1 } from '@standard-schema/spec'; -export const psql = createSqlTag(); +const tag = createSqlTag(); + +interface TemplateStringsArray extends ReadonlyArray { + readonly raw: readonly string[]; +} + +type CallableTag< + T extends StandardSchemaV1 = StandardSchemaV1, +> = (template: TemplateStringsArray, ...values: ValueExpression[]) => QuerySqlToken; + +export type TaggedTemplateLiteralInvocation = QuerySqlToken; + +function psqlFn(template: TemplateStringsArray, ...values: ValueExpression[]) { + return tag.type(z.unknown())(template, ...values); +} + +Object.assign(psqlFn, createSqlTag()); + +export const psql = psqlFn as any as SqlTag & CallableTag; diff --git a/packages/services/commerce/src/index.ts b/packages/services/commerce/src/index.ts index a951f45ed..e6073f1bd 100644 --- a/packages/services/commerce/src/index.ts +++ b/packages/services/commerce/src/index.ts @@ -59,7 +59,7 @@ async function main() { tracing ? [tracing.instrumentSlonik()] : undefined, ); - const taskScheduler = new TaskScheduler(storage.pool.getRawPgPool()); + const taskScheduler = new TaskScheduler(storage.pool); const usageEstimator = createEstimator({ logger: server.log, diff --git a/packages/services/server/src/index.ts b/packages/services/server/src/index.ts index 97d7f7595..c1d430915 100644 --- a/packages/services/server/src/index.ts +++ b/packages/services/server/src/index.ts @@ -169,7 +169,7 @@ export async function main() { 10, tracing ? [tracing.instrumentSlonik()] : [], ); - const taskScheduler = new TaskScheduler(storage.pool.getRawPgPool()); + const taskScheduler = new TaskScheduler(storage.pool); const redis = createRedisClient('Redis', env.redis, server.log.child({ source: 'Redis' })); diff --git a/packages/services/service-common/src/tracing.ts b/packages/services/service-common/src/tracing.ts index e7d970719..790284575 100644 --- a/packages/services/service-common/src/tracing.ts +++ b/packages/services/service-common/src/tracing.ts @@ -24,6 +24,7 @@ import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; import { Instrumentation, registerInstrumentations } from '@opentelemetry/instrumentation'; import { BatchSpanProcessor, + ReadableSpan, Sampler, SamplingDecision, SpanProcessor, @@ -63,7 +64,7 @@ export class TracingInstance { if (this.options.collectorEndpoint) { // Grafana endpoint const httpExporter = new OTLPTraceExporter({ url: this.options.collectorEndpoint }); - processors.push(new BatchSpanProcessor(httpExporter)); + processors.push(new ServiceBatchSpanProcessor(httpExporter)); } if (this.options.hiveTracing) { // Hive Tracing endpoint @@ -245,11 +246,12 @@ function extractParts(sqlStatement: string): { } export const createSlonikInterceptor = (options: SlonikTracingInterceptorOptions): Interceptor => { - const tracer = trace.getTracer('slonik'); + const tracer = trace.getTracer('slonik-service-common'); const connections: Record> = {}; const shouldExcludeFn = options.shouldExcludeStatement || (() => false); return { + name: 'slonik-tracing-interceptor', afterPoolConnection(context) { connections[context.connectionId] = {}; @@ -515,3 +517,9 @@ export function traceFn( } as any; }; } + +class ServiceBatchSpanProcessor extends BatchSpanProcessor { + onEnd(span: ReadableSpan): void { + return super.onEnd(span); + } +} diff --git a/packages/services/storage/package.json b/packages/services/storage/package.json index acb48c5b4..8dcdb3dc2 100644 --- a/packages/services/storage/package.json +++ b/packages/services/storage/package.json @@ -28,7 +28,6 @@ "got": "14.4.7", "graphql": "^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", "pg-promise": "11.10.2", - "slonik-utilities": "1.9.4", "tslib": "2.8.1", "typescript": "5.7.3", "zod": "3.25.76" diff --git a/packages/services/storage/src/index.ts b/packages/services/storage/src/index.ts index 5055cde3f..9e3d3b9a0 100644 --- a/packages/services/storage/src/index.ts +++ b/packages/services/storage/src/index.ts @@ -1,4 +1,3 @@ -import { update } from 'slonik-utilities'; import { z } from 'zod'; import type { Member, @@ -235,6 +234,21 @@ export async function createStorage( return roleId; }, + getOIDCIntegrationById(args: { oidcIntegrationId: string }, connection: CommonQueryMethods) { + return connection + .maybeOne( + psql`/* getOIDCIntegrationById */ + SELECT + ${oidcIntegrationFields()} + FROM + "oidc_integrations" + WHERE + "id" = ${args.oidcIntegrationId} + LIMIT 1 + `, + ) + .then(OIDCIntegrationModel.nullable().parse); + }, }; function buildUserData(input: { @@ -335,9 +349,12 @@ export async function createStorage( let invitation: OrganizationInvitation | null = null; if (oidcIntegration?.id) { - const oidcConfig = await this.getOIDCIntegrationById({ - oidcIntegrationId: oidcIntegration.id, - }); + const oidcConfig = await shared.getOIDCIntegrationById( + { + oidcIntegrationId: oidcIntegration.id, + }, + t, + ); if (oidcConfig) { invitation = await t @@ -363,10 +380,31 @@ export async function createStorage( if (oidcConfig?.requireInvitation && !invitation) { const member = internalUser - ? await this.getOrganizationMember({ - organizationId: oidcConfig.linkedOrganizationId, - userId: internalUser.id, - }) + ? await t + .maybeOne( + psql`/* getOrganizationMember */ + SELECT + ${userFields(psql`"u".`)}, + omr.scopes as scopes, + om.organization_id AS "organizationId", + om.connected_to_zendesk AS "connectedToZendesk", + CASE WHEN o.user_id = om.user_id THEN true ELSE false END AS "isOwner", + omr.id as "roleId", + omr.name as "roleName", + omr.locked as "roleLocked", + omr.scopes as "roleScopes", + omr.description as "roleDescription" + FROM organization_member as om + LEFT JOIN organizations as o ON (o.id = om.organization_id) + LEFT JOIN users as u ON (u.id = om.user_id) + LEFT JOIN organization_member_roles as omr ON (omr.organization_id = o.id AND omr.id = om.role_id) + WHERE + om.organization_id = ${oidcConfig.linkedOrganizationId} + AND om.user_id = ${internalUser.id} + ORDER BY u.created_at DESC + `, + ) + .then(MemberModel.nullable().parse) : null; if (!member) { @@ -617,7 +655,7 @@ export async function createStorage( psql`/* getOrganizationOwnerId */ SELECT id, user_id FROM organizations - WHERE id IN (${psql.join(organizations, psql`, `)})`, + WHERE id IN (${psql.join(organizations, psql.fragment`, `)})`, ) .then(z.array(OrganizationUserIdAndIdModel).parse); @@ -694,7 +732,7 @@ export async function createStorage( LEFT JOIN organization_member_roles as omr ON (omr.organization_id = o.id AND omr.id = om.role_id) WHERE (om.organization_id, om.user_id) IN ((${psql.join( selectors.map(s => psql`${s.organizationId}, ${s.userId}`), - psql`), (`, + psql.fragment`), (`, )})) ORDER BY u.created_at DESC `, @@ -836,7 +874,7 @@ export async function createStorage( LEFT JOIN organization_member_roles as omr ON (omr.organization_id = om.organization_id AND omr.id = om.role_id) WHERE (om.organization_id, om.user_id) IN ((${psql.join( pairs.map(p => psql`${p.organizationId}, ${p.userId}`), - psql`), (`, + psql.fragment`), (`, )})) `, ) @@ -1499,7 +1537,7 @@ export async function createStorage( (id, project_id) IN ( (${psql.join( uniqueSelectors.map(s => psql`${s.targetId}, ${s.projectId}`), - psql`), (`, + psql.fragment`), (`, )}) ) `, @@ -1706,7 +1744,7 @@ export async function createStorage( await trx.query(psql`/* deleteTargetValidation */ DELETE FROM target_validation - WHERE destination_target_id NOT IN (${psql.join(targets, psql`, `)}) + WHERE destination_target_id NOT IN (${psql.join(targets, psql.fragment`, `)}) AND target_id = ${target} `); @@ -1716,8 +1754,8 @@ export async function createStorage( VALUES ( ${psql.join( - targets.map(dest => psql.join([target, dest], psql`, `)), - psql`), (`, + targets.map(dest => psql.join([target, dest], psql.fragment`, `)), + psql.fragment`), (`, )} ) ON CONFLICT (target_id, destination_target_id) DO NOTHING @@ -2277,7 +2315,7 @@ export async function createStorage( const service = input.service ?? null; const output = await pool.transaction('createVersion', async trx => { - const log = await pool + const log = await trx .maybeOne( psql`/* createVersion */ INSERT INTO schema_log @@ -2416,7 +2454,7 @@ export async function createStorage( LEFT JOIN projects as p ON (p.id = sl.project_id) WHERE (sl.id, sl.target_id) IN ((${psql.join( selectors.map(s => psql`${s.commit}, ${s.targetId}`), - psql`), (`, + psql.fragment`), (`, )})) `, ); @@ -2526,7 +2564,7 @@ export async function createStorage( DELETE FROM alert_channels WHERE project_id = ${projectId} AND - id IN (${psql.join(channelIds, psql`, `)}) + id IN (${psql.join(channelIds, psql.fragment`, `)}) RETURNING ${alertChannelFields()} `, @@ -2570,7 +2608,7 @@ export async function createStorage( DELETE FROM alerts WHERE project_id = ${project} AND - id IN (${psql.join(alerts, psql`, `)}) + id IN (${psql.join(alerts, psql.fragment`, `)}) RETURNING ${alertFields()} `, @@ -2846,33 +2884,19 @@ export async function createStorage( ), ); }, - async completeGetStartedStep({ organizationId: organization, step }) { - await update( - pool.getSlonikPool(), - 'organizations', - { - [organizationGetStartedMapping[step]]: true, - }, - { - id: organization, - }, - ); + async completeGetStartedStep({ organizationId, step }) { + await pool.query(psql` + UPDATE + "organizations" + SET + ${psql.identifier([organizationGetStartedMapping[step]])} = True + WHERE + "organizations"."id" = ${organizationId} + `); }, - async getOIDCIntegrationById({ oidcIntegrationId: integrationId }) { - return await pool - .maybeOne( - psql`/* getOIDCIntegrationById */ - SELECT - ${oidcIntegrationFields()} - FROM - "oidc_integrations" - WHERE - "id" = ${integrationId} - LIMIT 1 - `, - ) - .then(OIDCIntegrationModel.nullable().parse); + async getOIDCIntegrationById(args) { + return shared.getOIDCIntegrationById(args, pool); }, getOIDCIntegrationForOrganization: batch(async selectors => { @@ -3060,7 +3084,7 @@ export async function createStorage( throw new Error('Role does not exist'); } - return await pool + return await trx .maybeOne( psql`/* updateOIDCDefaultMemberRole */ UPDATE "oidc_integrations" diff --git a/packages/services/storage/src/tokens.ts b/packages/services/storage/src/tokens.ts index 9440fa582..319a2eb71 100644 --- a/packages/services/storage/src/tokens.ts +++ b/packages/services/storage/src/tokens.ts @@ -141,7 +141,7 @@ export async function createTokenStorage( VALUES (${psql.join( tokens.map(t => psql`${t.token}, ${toDate(t.date)}`), - psql`), (`, + psql.fragment`), (`, )}) ) as c(token, last_used_at) WHERE c.token = t.token; diff --git a/packages/services/workflows/src/index.ts b/packages/services/workflows/src/index.ts index 305817bed..d14be17d5 100644 --- a/packages/services/workflows/src/index.ts +++ b/packages/services/workflows/src/index.ts @@ -57,7 +57,7 @@ const pg = await createPostgresDatabasePool({ }); const logger = new Logger({ level: env.log.level }); -logger.info({ pid: process.pid }, 'starting workflow service'); +logger.info({ pid: process.pid }, 'starting workflow service ' + process.pid); const stopHttpHeartbeat = env.httpHeartbeat ? startHeartbeats({ @@ -144,7 +144,7 @@ const shutdownMetrics = env.prometheus const runner = await run({ logger: bridgeGraphileLogger(logger), crontab, - pgPool: pg.getRawPgPool(), + pgPool: pg.getPgPoolCompat(), taskList: Object.fromEntries(modules.map(module => module.task(context))), noHandleSignals: true, events: createTaskEventEmitter(), diff --git a/packages/services/workflows/src/kit.ts b/packages/services/workflows/src/kit.ts index 987ba2e37..213933ed2 100644 --- a/packages/services/workflows/src/kit.ts +++ b/packages/services/workflows/src/kit.ts @@ -1,9 +1,9 @@ import { BentoCache, bentostore } from 'bentocache'; import { memoryDriver } from 'bentocache/build/src/drivers/memory'; import { makeWorkerUtils, WorkerUtils, type JobHelpers, type Task } from 'graphile-worker'; -import type { Pool } from 'pg'; import { z } from 'zod'; import { Logger } from '@graphql-hive/logger'; +import { PostgresDatabasePool, psql } from '@hive/postgres'; import { bridgeGraphileLogger } from '@hive/pubsub'; import type { Context } from './context'; @@ -76,11 +76,11 @@ export class TaskScheduler { cache: BentoCache<{ store: ReturnType }>; constructor( - pgPool: Pool, + private pgPool: PostgresDatabasePool, private logger: Logger = new Logger(), ) { this.tools = makeWorkerUtils({ - pgPool, + pgPool: pgPool.getPgPoolCompat(), logger: bridgeGraphileLogger(logger), }); this.cache = new BentoCache({ @@ -118,7 +118,6 @@ export class TaskScheduler { ); const input = taskDefinition.schema.parse(payload); - const tools = await this.tools; if (opts?.dedupe) { @@ -128,27 +127,26 @@ export class TaskScheduler { let shouldSkip = true; + const { pgPool } = this; + await this.cache.getOrSet({ key: `${taskDefinition.name}:${dedupeKey}`, ttl: opts.dedupe.ttl, async factory() { - return await tools.withPgClient(async client => { - const result = await client.query( - ` + const result = await pgPool.anyFirst( + psql` INSERT INTO "graphile_worker_deduplication" ("task_name", "dedupe_key", "expires_at") - VALUES($1, $2, $3) + VALUES(${taskDefinition.name}, ${dedupeKey}, ${expiresAt}) ON CONFLICT ("task_name", "dedupe_key") DO UPDATE SET "expires_at" = EXCLUDED.expires_at WHERE "graphile_worker_deduplication"."expires_at" < NOW() RETURNING xmax = 0 AS "inserted" `, - [taskDefinition.name, dedupeKey, expiresAt], - ); + ); - shouldSkip = result.rows.length === 0; - return true; - }); + shouldSkip = result.length === 0; + return true; }, }); @@ -176,8 +174,4 @@ export class TaskScheduler { 'task enqueued.', ); } - - async dispose() { - await (await this.tools).release(); - } } diff --git a/patches/@slonik__pg-driver.patch b/patches/@slonik__pg-driver.patch new file mode 100644 index 000000000..1c3ef7f14 --- /dev/null +++ b/patches/@slonik__pg-driver.patch @@ -0,0 +1,28 @@ +diff --git a/dist/factories/createPgDriverFactory.js b/dist/factories/createPgDriverFactory.js +index ed4601ac233435cc8ea5e546c722ede4bd8873d7..b57306c66a3e2176a6fda9d749f870a1b1158007 100644 +--- a/dist/factories/createPgDriverFactory.js ++++ b/dist/factories/createPgDriverFactory.js +@@ -230,7 +230,8 @@ export const createPgDriverFactory = () => { + }); + } + if (Array.isArray(result)) { +- throw new InvalidInputError("Must not use multiple statements in a single query."); ++ result = result[result.length - 1] ++ // throw new InvalidInputError("Must not use multiple statements in a single query."); + } + return { + command: result.command, +diff --git a/src/factories/createPgDriverFactory.ts b/src/factories/createPgDriverFactory.ts +index 608ed21b07e90fb67dc1d778a836e3554ff50671..c606167619280e58df26601ff1bc7a903dead48d 100644 +--- a/src/factories/createPgDriverFactory.ts ++++ b/src/factories/createPgDriverFactory.ts +@@ -314,7 +314,8 @@ export const createPgDriverFactory = (): DriverFactory => { + } + + if (Array.isArray(result)) { +- throw new InvalidInputError("Must not use multiple statements in a single query."); ++ result = result[result.length - 1] ++ // throw new InvalidInputError("Must not use multiple statements in a single query."); + } + + return { diff --git a/patches/slonik.patch b/patches/slonik.patch new file mode 100644 index 000000000..03b4ed9e0 --- /dev/null +++ b/patches/slonik.patch @@ -0,0 +1,242 @@ +diff --git a/dist/factories/createConnection.js b/dist/factories/createConnection.js +index 35b11d4d843c273aa4e0a5003cd1b8e082c41286..82bbb30c22b9f4bb7666f65afa483d71b33e51cd 100644 +--- a/dist/factories/createConnection.js ++++ b/dist/factories/createConnection.js +@@ -56,23 +56,8 @@ export const createConnection = async (parentLog, pool, clientConfiguration, con + for (const interceptor of clientConfiguration.interceptors) { + const beforePoolConnection = interceptor.beforePoolConnection; + if (beforePoolConnection) { +- const maybeNewPool = await tracer.startActiveSpan("slonik.interceptor.beforePoolConnection", async (interceptorSpan) => { +- interceptorSpan.setAttribute("interceptor.name", interceptor.name); +- try { +- return await beforePoolConnection(connectionContext); +- } +- catch (error) { +- interceptorSpan.recordException(error); +- interceptorSpan.setStatus({ +- code: SpanStatusCode.ERROR, +- message: String(error), +- }); +- throw error; +- } +- finally { +- interceptorSpan.end(); +- } +- }); ++ const maybeNewPool = await beforePoolConnection(connectionContext); ++ + if (maybeNewPool) { + return await poolHandler(maybeNewPool); + } +diff --git a/dist/factories/createConnectionPool.js b/dist/factories/createConnectionPool.js +index 5d7d7de147c6c3d9f835ba987c61d34f179bba9d..1df983bf044a9fa5081b19cd181663f897344e35 100644 +--- a/dist/factories/createConnectionPool.js ++++ b/dist/factories/createConnectionPool.js +@@ -291,13 +291,7 @@ export const createConnectionPool = ({ driver, events, idleTimeout, maximumConne + isEnded = true; + }; + const acquire = async () => { +- return tracer.startActiveSpan("slonik.connection.acquire", async (span) => { + try { +- span.setAttribute("slonik.pool.id", id); +- span.setAttribute("slonik.pool.connections.total", connections.size); +- span.setAttribute("slonik.pool.connections.pending", pendingConnections.size); +- span.setAttribute("slonik.pool.waitingClients", waitingClients.length); +- span.setAttribute("slonik.pool.maximumSize", maximumPoolSize); + if (isEnded) { + span.setStatus({ + code: SpanStatusCode.ERROR, +@@ -368,17 +362,11 @@ export const createConnectionPool = ({ driver, events, idleTimeout, maximumConne + } + } + if (idleConnection) { +- span.setAttribute("slonik.connection.id", idleConnection.id()); +- span.setAttribute("method", "acquire:reuse-idle"); +- span.setStatus({ code: SpanStatusCode.OK }); + return idleConnection; + } + if (pendingConnections.size + activeConnectionCount() - destroyedInLoop < maximumPoolSize) { +- const newConnection = await addConnection(span); ++ const newConnection = await addConnection(/*span*/); + newConnection.acquire(); +- span.setAttribute("slonik.connection.id", newConnection.id()); +- span.setAttribute("method", "acquire:add-connection"); +- span.setStatus({ code: SpanStatusCode.OK }); + return newConnection; + } + if (isEnding || isEnded) { +@@ -406,25 +394,12 @@ export const createConnectionPool = ({ driver, events, idleTimeout, maximumConne + connectionId: connection.id(), + duration: Number(process.hrtime.bigint() - queuedAt) / 1e6, + }, "connection has been acquired from the queue"); +- span.setAttribute("queuedMs", Number(process.hrtime.bigint() - queuedAt) / 1e6); +- span.setAttribute("slonik.connection.id", connection.id()); +- span.setAttribute("method", "acquire:queued"); +- span.setStatus({ code: SpanStatusCode.OK }); + return connection; + }); + } + catch (error) { +- span.recordException(error); +- span.setStatus({ +- code: SpanStatusCode.ERROR, +- message: error instanceof Error ? error.message : String(error), +- }); + throw error; + } +- finally { +- span.end(); +- } +- }); + }; + const warmup = async () => { + if (minimumPoolSize <= 0 || isEnding || isEnded) { +diff --git a/dist/routines/establishConnection.js b/dist/routines/establishConnection.js +index cec0de8d60542a48c47656b0d706de641d4e184e..6ca217362c42878449d1933de4953e3bb0612730 100644 +--- a/dist/routines/establishConnection.js ++++ b/dist/routines/establishConnection.js +@@ -5,7 +5,6 @@ import { generateUid } from "@slonik/utilities"; + import { serializeError } from "serialize-error"; + const tracer = trace.getTracer("slonik"); + export const establishConnection = async (parentLog, pool, connectionRetryLimit) => { +- return await tracer.startActiveSpan("slonik.acquireConnection", async (span) => { + let connection; + let remainingConnectionRetryLimit = connectionRetryLimit + 1; + while (true) { +@@ -31,11 +30,6 @@ export const establishConnection = async (parentLog, pool, connectionRetryLimit) + continue; + } + else { +- span.recordException(error); +- span.setStatus({ +- code: SpanStatusCode.ERROR, +- message: String(error), +- }); + throw new ConnectionError(error.message, { + cause: error, + }); +@@ -45,8 +39,6 @@ export const establishConnection = async (parentLog, pool, connectionRetryLimit) + if (!connection) { + throw new UnexpectedStateError("Connection handle is not present."); + } +- span.end(); + return connection; +- }); + }; + //# sourceMappingURL=establishConnection.js.map +\ No newline at end of file +diff --git a/dist/routines/executeQuery.js b/dist/routines/executeQuery.js +index 7446ec489a4fa4728be4e4abaa2495592bf9da9f..9256a675171fea8b42ce5a3895620472d7ff6eb7 100644 +--- a/dist/routines/executeQuery.js ++++ b/dist/routines/executeQuery.js +@@ -145,26 +145,8 @@ const executeQueryInternal = async (connectionLogger, connection, clientConfigur + for (const interceptor of clientConfiguration.interceptors) { + const beforeQueryExecution = interceptor.beforeQueryExecution; + if (beforeQueryExecution) { +- result = await tracer.startActiveSpan("slonik.interceptor.beforeQueryExecution", { +- attributes: { +- "interceptor.name": interceptor.name, +- }, +- }, async (span) => { +- try { +- return await beforeQueryExecution(executionContext, actualQuery); +- } +- catch (error) { +- span.recordException(error); +- span.setStatus({ +- code: SpanStatusCode.ERROR, +- message: String(error), +- }); +- throw error; +- } +- finally { +- span.end(); +- } +- }); ++ result = await beforeQueryExecution(executionContext, actualQuery); ++ + if (result) { + log.info("beforeQueryExecution interceptor produced a result; short-circuiting query execution using beforeQueryExecution result"); + return result; +@@ -315,26 +297,7 @@ const executeQueryInternal = async (connectionLogger, connection, clientConfigur + for (const interceptor of interceptors) { + const afterQueryExecution = interceptor.afterQueryExecution; + if (afterQueryExecution) { +- await tracer.startActiveSpan("slonik.interceptor.afterQueryExecution", { +- attributes: { +- "interceptor.name": interceptor.name, +- }, +- }, async (span) => { +- try { +- await afterQueryExecution(executionContext, actualQuery, result); +- } +- catch (error) { +- span.recordException(error); +- span.setStatus({ +- code: SpanStatusCode.ERROR, +- message: String(error), +- }); +- throw error; +- } +- finally { +- span.end(); +- } +- }); ++ await afterQueryExecution(executionContext, actualQuery, result); + } + } + for (const interceptor of interceptors) { +@@ -405,50 +368,12 @@ const executeQueryInternal = async (connectionLogger, connection, clientConfigur + for (const interceptor of interceptors) { + const beforeQueryResult = interceptor.beforeQueryResult; + if (beforeQueryResult) { +- await tracer.startActiveSpan("slonik.interceptor.beforeQueryResult", { +- attributes: { +- "interceptor.name": interceptor.name, +- }, +- }, async (span) => { +- try { +- await beforeQueryResult(executionContext, actualQuery, result); +- } +- catch (error) { +- span.recordException(error); +- span.setStatus({ +- code: SpanStatusCode.ERROR, +- message: String(error), +- }); +- throw error; +- } +- finally { +- span.end(); +- } +- }); ++ await beforeQueryResult(executionContext, actualQuery, result); + } + } + return result; + }; + export const executeQuery = async (connectionLogger, connection, clientConfiguration, query, inheritedQueryId, executionRoutine, stream, integrityValidation) => { +- return await tracer.startActiveSpan("slonik.executeQuery", { +- attributes: { +- sql: query.sql, +- }, +- }, async (span) => { +- try { +- return await executeQueryInternal(connectionLogger, connection, clientConfiguration, query, inheritedQueryId, executionRoutine, integrityValidation, stream); +- } +- catch (error) { +- span.recordException(error); +- span.setStatus({ +- code: SpanStatusCode.ERROR, +- message: String(error), +- }); +- throw error; +- } +- finally { +- span.end(); +- } +- }); ++ return await executeQueryInternal(connectionLogger, connection, clientConfiguration, query, inheritedQueryId, executionRoutine, integrityValidation, stream); + }; + //# sourceMappingURL=executeQuery.js.map +\ No newline at end of file diff --git a/patches/slonik@30.4.4.patch b/patches/slonik@30.4.4.patch deleted file mode 100644 index 472e2b326..000000000 --- a/patches/slonik@30.4.4.patch +++ /dev/null @@ -1,69 +0,0 @@ -diff --git a/dist/src/binders/bindPool.js b/dist/src/binders/bindPool.js -index ad509058bf5d26c82d4b2aea35e945df2f83f38e..1ca27403f70362f9a60abef7df6c2fdaaba1805b 100644 ---- a/dist/src/binders/bindPool.js -+++ b/dist/src/binders/bindPool.js -@@ -7,6 +7,7 @@ const factories_1 = require("../factories"); - const state_1 = require("../state"); - const bindPool = (parentLog, pool, clientConfiguration) => { - return { -+ pool, - any: (query) => { - return (0, factories_1.createConnection)(parentLog, pool, clientConfiguration, 'IMPLICIT_QUERY', (connectionLog, connection, boundConnection) => { - return boundConnection.any(query); -diff --git a/dist/src/factories/createPool.js b/dist/src/factories/createPool.js -index b91a9fe433dc340f5cdf096ca4c568297c343ab3..401df1272d1c7f344bb956b38cc7dbde29231742 100644 ---- a/dist/src/factories/createPool.js -+++ b/dist/src/factories/createPool.js -@@ -44,6 +44,16 @@ const createPool = async (connectionUri, clientConfigurationInput) => { - getTypeParser, - }, - }); -+ -+ // https://github.com/gajus/slonik/issues/471 -+ // https://github.com/brianc/node-postgres/issues/2764#issuecomment-1163475426 -+ // Slonik did not have a way to handle errors emitted by the pool, which resulted in an uncaught exception, which would crash the process. -+ pool.on('error', (error) => { -+ poolLog.error({ -+ error: (0, serialize_error_1.serializeError)(error), -+ }, 'client error'); -+ }); -+ - state_1.poolStateMap.set(pool, { - ended: false, - mock: false, -diff --git a/dist/src/types.d.ts b/dist/src/types.d.ts -index d091b301b1df0f8d9ad9298d587081fe6d33c0be..57ea5a46fbf0878546e34debed2401efcb66d7fb 100644 ---- a/dist/src/types.d.ts -+++ b/dist/src/types.d.ts -@@ -138,6 +138,7 @@ export declare type DatabasePool = CommonQueryMethods & { - readonly end: () => Promise; - readonly getPoolState: () => PoolState; - readonly stream: StreamFunction; -+ readonly pool: PgPool; - }; - export declare type DatabaseConnection = DatabasePool | DatabasePoolConnection; - export declare type QueryResultRowColumn = PrimitiveValueExpression; -diff --git a/src/binders/bindPool.ts b/src/binders/bindPool.ts -index d10bb50117b613f262ee715fc40745e8270a60b3..fde977dd042ec2561163f252c7f76f92cb043eb0 100644 ---- a/src/binders/bindPool.ts -+++ b/src/binders/bindPool.ts -@@ -26,6 +26,7 @@ export const bindPool = ( - clientConfiguration: ClientConfiguration, - ): DatabasePool => { - return { -+ pool, - any: (query: TaggedTemplateLiteralInvocation) => { - return createConnection( - parentLog, -diff --git a/src/types.ts b/src/types.ts -index da293a0a4ce2583c43711cbe90d4829ec9c46fa8..962acdd5c2652e6e61147adc03204bca065cd28c 100644 ---- a/src/types.ts -+++ b/src/types.ts -@@ -191,6 +191,7 @@ export type DatabasePool = CommonQueryMethods & { - readonly end: () => Promise, - readonly getPoolState: () => PoolState, - readonly stream: StreamFunction, -+ readonly pool: PgPool, - }; - - export type DatabaseConnection = DatabasePool | DatabasePoolConnection; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6b3eea503..2d75a6ad1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -64,6 +64,9 @@ patchedDependencies: '@oclif/core@4.0.6': hash: d1dc04fa0009203663f00e051630035b556b6bd06913c6ab45aeee47ccdedd46 path: patches/@oclif__core@4.0.6.patch + '@slonik/pg-driver': + hash: d281baceb3dc04c2d6348d8b2bb1f5598bef4e24a6a173fc0048c2b3ab6e27ea + path: patches/@slonik__pg-driver.patch '@theguild/editor@1.2.5': hash: a401455daa519af0fe686b4f970a02582f9e406c520aad19273a8eeef8f4adf7 path: patches/@theguild__editor@1.2.5.patch @@ -88,9 +91,9 @@ patchedDependencies: p-cancelable@4.0.1: hash: 195ae63d47810ca4c987421948f15869356e598dccde9e8c9a4ff4efd3e88322 path: patches/p-cancelable@4.0.1.patch - slonik@30.4.4: - hash: 195b140c0181c27a85a6026c0058087a419e38f6c5d89f5f2c608e39f5bf23e9 - path: patches/slonik@30.4.4.patch + slonik: + hash: e16e74ebc8c5f42be257d540b5ac7d175ff3264481046b5c33fc647c9997acdc + path: patches/slonik.patch importers: @@ -433,12 +436,21 @@ importers: packages/internal/postgres: dependencies: + '@standard-schema/spec': + specifier: 1.0.0 + version: 1.0.0 slonik: - specifier: 30.4.4 - version: 30.4.4(patch_hash=195b140c0181c27a85a6026c0058087a419e38f6c5d89f5f2c608e39f5bf23e9) + specifier: 48.13.2 + version: 48.13.2(patch_hash=e16e74ebc8c5f42be257d540b5ac7d175ff3264481046b5c33fc647c9997acdc) slonik-interceptor-query-logging: - specifier: 46.4.0 - version: 46.4.0(slonik@30.4.4(patch_hash=195b140c0181c27a85a6026c0058087a419e38f6c5d89f5f2c608e39f5bf23e9)) + specifier: 48.13.2 + version: 48.13.2(slonik@48.13.2(patch_hash=e16e74ebc8c5f42be257d540b5ac7d175ff3264481046b5c33fc647c9997acdc)) + slonik-sql-tag-raw: + specifier: 48.13.2 + version: 48.13.2(slonik@48.13.2(patch_hash=e16e74ebc8c5f42be257d540b5ac7d175ff3264481046b5c33fc647c9997acdc)) + zod: + specifier: 3.25.76 + version: 3.25.76 devDependencies: '@hive/service-common': specifier: workspace:* @@ -1051,7 +1063,7 @@ importers: version: 6.2.0 pg-promise: specifier: 11.10.2 - version: 11.10.2(pg-query-stream@4.7.0(pg@8.13.1)) + version: 11.10.2(pg-query-stream@4.14.0(pg@8.20.0)) tslib: specifier: 2.8.1 version: 2.8.1 @@ -1795,10 +1807,7 @@ importers: version: 16.9.0 pg-promise: specifier: 11.10.2 - version: 11.10.2(pg-query-stream@4.7.0(pg@8.13.1)) - slonik-utilities: - specifier: 1.9.4 - version: 1.9.4 + version: 11.10.2(pg-query-stream@4.14.0(pg@8.20.0)) tslib: specifier: 2.8.1 version: 2.8.1 @@ -8805,6 +8814,38 @@ packages: resolution: {integrity: sha512-kT+07JvOqpYH3b/ttVo3iqKIFiHV2NKmD6QUc/F7HrjCgSdSA10zxqi0euXEF2prB49OU7SfjadzQ0WhNc7tiw==} engines: {node: '>= 18', npm: '>= 8.6.0'} + '@slonik/driver@48.13.2': + resolution: {integrity: sha512-vNywhgnCvaHejW/bGsZglUXGf7LXxin6aZ+iIefuf6p1NWcT/nuyb38QrpB+cveLoho1iVaPdECnk5ksnFbKCw==} + engines: {node: '>=24'} + peerDependencies: + zod: ^3.25 || ^4 + + '@slonik/errors@48.13.2': + resolution: {integrity: sha512-rUeL6pMVRCK6TzYku0QxFvSmzk4bkbpg3348A7qaTzFEUtX9pmaD0aGXBpc1si9+cVscckRl/ezy9TTRDODPCw==} + engines: {node: '>=24'} + + '@slonik/pg-driver@48.13.2': + resolution: {integrity: sha512-ULEWssW52X4sxxqBWtKYw0T1sSPgPm58wDmYo8xocZC5JuXFHvMzHpUk3X0lMUUXdOXKGcIL/G5sWG74PWi1Hg==} + engines: {node: '>=24'} + peerDependencies: + zod: ^3.25 || ^4 + + '@slonik/sql-tag@48.13.2': + resolution: {integrity: sha512-PWHIOTV5r7QZB8E7LNPCPOH+gTocAKne6P/JpPbSa16xynsABTcWLaa3yF4Z6oAbwCgzAkQdJ1TIubd4hawQ1A==} + engines: {node: '>=24'} + + '@slonik/types@48.13.2': + resolution: {integrity: sha512-04iyM4iz68WoYN/9IcQdlwam2vF7QG1QuKfXYJLWTY6sIFMMQgTdlfPpOE7Q5N2UNy7twQXvjoyYJMGYNu/sCA==} + engines: {node: '>=24'} + peerDependencies: + zod: ^3.25 || ^4 + + '@slonik/utilities@48.13.2': + resolution: {integrity: sha512-KfAKm5P6dsQoTaq3GbJD6ybFS9H18zrwZ3FpNbs4u63NrpIMNas6ZWqdeEz+Pqy+OKpQWxiy1x06S/PBVNJSFA==} + engines: {node: '>=24'} + peerDependencies: + zod: ^3.25 || ^4 + '@smithy/abort-controller@4.0.0': resolution: {integrity: sha512-xFNL1ZfluscKiVI0qlPEnu7pL1UgNNIzQdjTPkaO7JCJtIkbArPYNtqbxohuNaQdksJ01Tn1wLbDA5oIp62P8w==} engines: {node: '>=18.0.0'} @@ -11173,10 +11214,6 @@ packages: buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - bufferput@0.1.3: - resolution: {integrity: sha512-nmPV88vDNzf0VMU1bdQ4A1oBlRR9y+CXfwWKfyKUgI2ZIkvreNzLMM3tkz0Lapb6f+Cz1V001UWRBsoGVCjqdw==} - engines: {node: '>=0.3.0'} - bufrw@1.4.0: resolution: {integrity: sha512-sWm8iPbqvL9+5SiYxXH73UOkyEbGQg7kyHQmReF89WJHQJw2eV4P/yZ0E+b71cczJ4pPobVhXxgQcmfSTgGHxQ==} engines: {node: '>= 0.10.x'} @@ -11697,9 +11734,6 @@ packages: core-js-compat@3.49.0: resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==} - core-js@3.25.5: - resolution: {integrity: sha512-nbm6eZSjm+ZuBQxCUPQKQCoUEfFOXjUZ8dTTyikyKaWrTYmAVbykQfwsKE5dBK88u3QCkCrzsx/PPlKfhsvgpw==} - core-util-is@1.0.2: resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} @@ -12053,10 +12087,6 @@ packages: resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} engines: {node: '>= 14'} - delay@4.4.1: - resolution: {integrity: sha512-aL3AhqtfhOlT/3ai6sWXeqwnw63ATNpnUiN4HL7x9q+My5QtHlO3OIkasmug9LKzpheLdmUKGRKnYXYAS7FQkQ==} - engines: {node: '>=6'} - delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -12362,9 +12392,6 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} - es6-error@4.1.1: - resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} - esast-util-from-estree@2.0.0: resolution: {integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==} @@ -12790,10 +12817,6 @@ packages: fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - fast-json-stringify@2.7.13: - resolution: {integrity: sha512-ar+hQ4+OIurUGjSJD1anvYSDcUflywhKjfxnsW4TBTD7+u0tJufv6DKRWoQk3vI6YBOWMoz0TQtfbe7dxbQmvA==} - engines: {node: '>= 10.0.0'} - fast-json-stringify@5.16.1: resolution: {integrity: sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g==} @@ -13160,10 +13183,6 @@ packages: get-source@2.0.12: resolution: {integrity: sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==} - get-stack-trace@2.1.1: - resolution: {integrity: sha512-dhqSDD9lHU/6FvIZ9KbXGmVK6IKr9ZskZtNOUvhlCiONlnqatu4FmAeRbxCfJJVuQ0NWfz6dAbibKQg19B7AmQ==} - engines: {node: '>=8.0'} - get-stdin@8.0.0: resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} engines: {node: '>=10'} @@ -13712,9 +13731,6 @@ packages: humps@2.0.1: resolution: {integrity: sha512-E0eIbrFWUhwfXJmsbdjRQFQPrl5pTEoKlz163j1mTqqUnU9PgR4AgB8AIITzuB3vLBdxZXyZ9TDIrwB2OASz4g==} - hyperid@2.3.1: - resolution: {integrity: sha512-mIbI7Ymn6MCdODaW1/6wdf5lvvXzmPsARN4zTLakMmcziBOuP4PxCBJvHF6kbAIHX6H4vAELx/pDmt0j6Th5RQ==} - hyperid@3.3.0: resolution: {integrity: sha512-7qhCVT4MJIoEsNcbhglhdmBKb09QtcmJNiIQGq7js/Khf5FtQQ9bzcAuloeqBeee7XD7JqDeve9KNlQya5tSGQ==} @@ -13855,10 +13871,6 @@ packages: inline-style-parser@0.2.4: resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==} - int64-buffer@0.99.1007: - resolution: {integrity: sha512-XDBEu44oSTqlvCSiOZ/0FoUkpWu/vwjJLGSKDabNISPQNZ5wub1FodGHBljRsrR0IXRPq7SslshZYMuA55CgTQ==} - engines: {node: '>= 4.5.0'} - internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} @@ -14108,10 +14120,6 @@ packages: resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} engines: {node: '>=0.10.0'} - is-plain-object@5.0.0: - resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} - engines: {node: '>=0.10.0'} - is-primitive@3.0.1: resolution: {integrity: sha512-GljRxhWvlCNRfZyORiH77FwdFwGcMO620o37EOYC0ORWdq+WYNVqW0w2Juzew4M+L81l6/QS3t5gkkihyRqv9w==} engines: {node: '>=0.10.0'} @@ -14260,8 +14268,8 @@ packages: resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} engines: {node: '>=16'} - iso8601-duration@1.3.0: - resolution: {integrity: sha512-K4CiUBzo3YeWk76FuET/dQPH03WE04R94feo5TSKQCXpoXQt9E4yx2CnY737QZnSAI3PI4WlKo/zfqizGx52QQ==} + iso8601-duration@2.1.3: + resolution: {integrity: sha512-OwkROKDXYhqKTl9uyB+/+lQ/Tx+L9LVb9tNRcsO4LtCSBDrmYbzyJLg9rGjYKAPDabD6IVdjMyUnnULHpejrCg==} isobject@3.0.1: resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} @@ -15509,9 +15517,6 @@ packages: muggle-string@0.4.1: resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} - multi-fork@0.0.2: - resolution: {integrity: sha512-SHWGuze0cZNiH+JGJQFlB1k7kZLGFCvW1Xo5Fcpe86KICkC3aVTJWpjUcmyYcLCB0I6gdzKLCia/bTIw2ggl8A==} - mustache@4.2.0: resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} hasBin: true @@ -15874,10 +15879,6 @@ packages: resolution: {integrity: sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==} engines: {node: '>=14.16'} - p-defer@3.0.0: - resolution: {integrity: sha512-ugZxsxmtTln604yeYd29EGrNhazN2lywetzpKhfmQjW/VJmhpDmWbiX+h0zL8V91R0UXkhb3KtPmyq9PZw3aYw==} - engines: {node: '>=8'} - p-filter@2.1.0: resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} engines: {node: '>=8'} @@ -16007,9 +16008,9 @@ packages: resolution: {integrity: sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw==} engines: {node: '>=16'} - parse-ms@2.1.0: - resolution: {integrity: sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==} - engines: {node: '>=6'} + parse-ms@4.0.0: + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + engines: {node: '>=18'} parse-node-version@1.0.1: resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} @@ -16118,17 +16119,17 @@ packages: pg-cloudflare@1.1.1: resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} + pg-cloudflare@1.3.0: + resolution: {integrity: sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==} + + pg-connection-string@2.12.0: + resolution: {integrity: sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==} + pg-connection-string@2.9.1: resolution: {integrity: sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==} - pg-copy-streams-binary@2.2.0: - resolution: {integrity: sha512-jPCWgTR8004wz5XOI2sc09+IMwE7YMeINYCabwPMCPtlgj2ay81VLCClMkj/u+xOeisRcN8vCrIZ4FrqlaTyBQ==} - - pg-copy-streams@6.0.4: - resolution: {integrity: sha512-FH6q2nFo0n2cFacLyIKorjDz8AOYtxrAANx1XMvYbKWNM2geY731gZstuP4mXMlqO6urRl9oIscFxf3GMIg3Ng==} - - pg-cursor@2.18.0: - resolution: {integrity: sha512-WkMubzXP+FWDIC6XfA9pZwJHO0rmUwbNXUNFfBshp9amnCraQslVLYqEuWA+7qemtyz+v3zybcvcX//Dq5WpxQ==} + pg-cursor@2.19.0: + resolution: {integrity: sha512-J5cF1MUz7LRJ9emOqF/06QjabMHMZy587rSPF0UuA8rCwKeeYl2co8Pp+6k5UU9YrAYHMzWkLxilfZB0hqsWWw==} peerDependencies: pg: ^8 @@ -16144,6 +16145,11 @@ packages: resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==} engines: {node: '>=4'} + pg-pool@3.13.0: + resolution: {integrity: sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==} + peerDependencies: + pg: '>=8.0' + pg-pool@3.7.0: resolution: {integrity: sha512-ZOBQForurqh4zZWjrgSwwAtzJ7QiRX0ovFkZr2klsen3Nm0aoh33Ls0fzfv3imeH/nw/O27cjdz5kzYJfeGp/g==} peerDependencies: @@ -16155,11 +16161,14 @@ packages: peerDependencies: pg-query-stream: 4.7.1 + pg-protocol@1.13.0: + resolution: {integrity: sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==} + pg-protocol@1.7.0: resolution: {integrity: sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==} - pg-query-stream@4.7.0: - resolution: {integrity: sha512-aQpK8yfFTvOzvPmhXEzWfkwM24lv2Y3TfFY0HJYwx0YM/2fL4DhqpBhLni2Kd+l9p/XoDEi+HFvEvOCm7oqaLg==} + pg-query-stream@4.14.0: + resolution: {integrity: sha512-B1LLxgqngAATPciOPYYKyaQfsw5wyP6BZq6nHqQOC5QaaEBsfW/0OBwWUga+knCAqENMeoow9I8Zgi2m3P9rWw==} peerDependencies: pg: ^8 @@ -16167,8 +16176,8 @@ packages: resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} engines: {node: '>=4'} - pg-types@4.0.1: - resolution: {integrity: sha512-hRCSDuLII9/LE3smys1hRHcu5QGcLs9ggT7I/TCs0IE+2Eesxi9+9RWAAwZ0yaGjxoWICF/YHLOEjydGujoJ+g==} + pg-types@4.1.0: + resolution: {integrity: sha512-o2XFanIMy/3+mThw69O8d4n1E5zsLhdO+OPqswezu7Z5ekP4hYDqlDjlmOpYMbzY2Br0ufCwJLdDIXeNVwcWFg==} engines: {node: '>=10'} pg@8.13.1: @@ -16180,6 +16189,15 @@ packages: pg-native: optional: true + pg@8.20.0: + resolution: {integrity: sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==} + engines: {node: '>= 16.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + pgpass@1.0.5: resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} @@ -16317,8 +16335,8 @@ packages: resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} engines: {node: '>=4'} - postgres-array@3.0.1: - resolution: {integrity: sha512-h7i53Dw2Yq3a1uuZ6lbVFAkvMMwssJ8jkzeAg0XaZm1XIFF/t/s+tockdqbWTymyEm07dVenOQbFisEi+kj8uA==} + postgres-array@3.0.4: + resolution: {integrity: sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==} engines: {node: '>=12'} postgres-bytea@1.0.0: @@ -16333,8 +16351,8 @@ packages: resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} engines: {node: '>=0.10.0'} - postgres-date@2.0.1: - resolution: {integrity: sha512-YtMKdsDt5Ojv1wQRvUhnyDJNSr2dGIC96mQVKz7xufp07nfuFONzdaowrMHjlAzY6GDLd4f+LUHHAAM1h4MdUw==} + postgres-date@2.1.0: + resolution: {integrity: sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==} engines: {node: '>=12'} postgres-interval@1.2.0: @@ -16345,8 +16363,8 @@ packages: resolution: {integrity: sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==} engines: {node: '>=12'} - postgres-interval@4.0.0: - resolution: {integrity: sha512-OWeL7kyEKJiY7mCmVY+c7/6uhAlt/colA/Nl/Mgls/M3jssrQzFra04iNWnD/qAmG7TsCSgWAASCyiaoBOP/sg==} + postgres-interval@4.0.2: + resolution: {integrity: sha512-EMsphSQ1YkQqKZL2cuG0zHkmjCCzQqQ71l2GXITqRwjhRleCdv00bDk/ktaSi0LnlaPzAc3535KTrjXsTdtx7A==} engines: {node: '>=12'} postgres-range@1.1.3: @@ -16452,9 +16470,9 @@ packages: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - pretty-ms@7.0.1: - resolution: {integrity: sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==} - engines: {node: '>=10'} + pretty-ms@9.3.0: + resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} + engines: {node: '>=18'} pretty-quick@4.0.0: resolution: {integrity: sha512-M+2MmeufXb/M7Xw3Afh1gxcYpj+sK0AxEfnfF958ktFeAyi5MsKY5brymVURQLgPLV1QaF5P4pb2oFJ54H3yzQ==} @@ -17099,9 +17117,9 @@ packages: engines: {node: 20 || >=22} hasBin: true - roarr@7.14.3: - resolution: {integrity: sha512-AvUQY27C6/biXEAyYUXc8ONBtP1cA3MQM88e24Fmsl3LAqtNR309nMaWFALYk7ORTqgGrgrjBJ1vE20DZAc5qA==} - engines: {node: '>=12.0'} + roarr@7.21.4: + resolution: {integrity: sha512-qvfUKCrpPzhWmQ4NxRYnuwhkI5lwmObhBU06BCK/lpj6PID9nL4Hk6XDwek2foKI+TMaV+Yw//XZshGF2Lox/Q==} + engines: {node: '>=18.0'} rolldown-vite@7.1.14: resolution: {integrity: sha512-eSiiRJmovt8qDJkGyZuLnbxAOAdie6NCmmd0NkTC0RJI9duiSBTfr8X2mBYJOUFzxQa2USaHmL99J9uMxkjCyw==} @@ -17278,13 +17296,9 @@ packages: sentence-case@3.0.4: resolution: {integrity: sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==} - serialize-error@5.0.0: - resolution: {integrity: sha512-/VtpuyzYf82mHYTtI4QKtwHa79vAdU5OQpNPAmE/0UDdlGT0ZxHwC+J6gXkw29wwoVI8fMPsfcVHOwXtUQYYQA==} - engines: {node: '>=8'} - - serialize-error@8.1.0: - resolution: {integrity: sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==} - engines: {node: '>=10'} + serialize-error@12.0.0: + resolution: {integrity: sha512-ZYkZLAvKTKQXWuh5XpBw7CdbSzagarX39WyZ2H07CDLC5/KfsRGlIXV8d4+tfqX1M7916mRqR1QfNHSij+c9Pw==} + engines: {node: '>=18'} seroval-plugins@1.3.3: resolution: {integrity: sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w==} @@ -17438,21 +17452,21 @@ packages: slick@1.12.2: resolution: {integrity: sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==} - slonik-interceptor-query-logging@46.4.0: - resolution: {integrity: sha512-OVJgY4B4JTynvwc+CrdvYKRZgkqNQ6OyFgh/yf880Chzi2cFcINaXYOq/1fIccAFDXVhxbMsq7M+FjEdlz/ITQ==} - engines: {node: '>=18'} + slonik-interceptor-query-logging@48.13.2: + resolution: {integrity: sha512-/Ib0eIj7pIki+czELuMFvP6z5FWH4m5pCkhWR1QbiCxDB7Zmf705bSkWcux2iChTu2WcWVcAnBdcdk/iZS9fpw==} + engines: {node: '>=24'} peerDependencies: slonik: '>=45.0.0' - slonik-utilities@1.9.4: - resolution: {integrity: sha512-kKu6y7cobAyoWfwaS6zZF7oqosxQ+iEBpQYD3YFv+B/jd/YM+4XxFidintA3CGnmq3R4EmXnPMsyy8IpKeE/Hg==} - engines: {node: '>=8.0'} + slonik-sql-tag-raw@48.13.2: + resolution: {integrity: sha512-1bMik7cU7usGIiOtn3Yjzw8p+enS/mAAjzwDyYoeoIUiEivUJytMBBZMP0V7JaeI5BeE89jRCx5LLDUFk3Tqhg==} + engines: {node: '>=24'} peerDependencies: - slonik: ^28 | ^29 + slonik: '>=45.0.0' - slonik@30.4.4: - resolution: {integrity: sha512-5Z1QJhRCDyq0J+0yiUN6COETKtvrYdmukeQn5RZUSt7EvzYo4oTm7D4j2ZV4DN0DMHsaQBSnW/tIgX+UHRJYmQ==} - engines: {node: '>=10.0'} + slonik@48.13.2: + resolution: {integrity: sha512-NU+pTfVJAIPTMcWlMYhFobuAQHM0orXlEWcDxrUVvCLEf+NvzB4nrlih7w6XHIf5RLRDq2zghzWuGSGagBe7HQ==} + engines: {node: '>=24'} slugify@1.6.6: resolution: {integrity: sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==} @@ -17635,6 +17649,9 @@ packages: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} + strict-event-emitter-types@2.0.0: + resolution: {integrity: sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA==} + strict-event-emitter@0.5.1: resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} @@ -17645,10 +17662,6 @@ packages: string-env-interpolation@1.0.1: resolution: {integrity: sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg==} - string-similarity@4.0.4: - resolution: {integrity: sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ==} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - string-template@0.2.1: resolution: {integrity: sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw==} @@ -17916,12 +17929,6 @@ packages: through2@2.0.5: resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} - through2@3.0.2: - resolution: {integrity: sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==} - - through2@4.0.2: - resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} - through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} @@ -18253,6 +18260,10 @@ packages: resolution: {integrity: sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==} engines: {node: '>=16'} + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + type-fest@5.4.2: resolution: {integrity: sha512-FLEenlVYf7Zcd34ISMLo3ZzRE1gRjY1nMDTp+bQRBiPsaKyIW8K3Zr99ioHDUgA9OGuGGJPyYpNcffGmBhJfGg==} engines: {node: '>=20'} @@ -29332,6 +29343,55 @@ snapshots: transitivePeerDependencies: - debug + '@slonik/driver@48.13.2(zod@4.3.6)': + dependencies: + '@slonik/types': 48.13.2(zod@4.3.6) + '@slonik/utilities': 48.13.2(zod@4.3.6) + roarr: 7.21.4 + serialize-error: 12.0.0 + strict-event-emitter-types: 2.0.0 + zod: 4.3.6 + + '@slonik/errors@48.13.2(zod@4.3.6)': + dependencies: + '@slonik/types': 48.13.2(zod@4.3.6) + transitivePeerDependencies: + - zod + + '@slonik/pg-driver@48.13.2(patch_hash=d281baceb3dc04c2d6348d8b2bb1f5598bef4e24a6a173fc0048c2b3ab6e27ea)(zod@4.3.6)': + dependencies: + '@slonik/driver': 48.13.2(zod@4.3.6) + '@slonik/errors': 48.13.2(zod@4.3.6) + '@slonik/sql-tag': 48.13.2 + '@slonik/types': 48.13.2(zod@4.3.6) + '@slonik/utilities': 48.13.2(zod@4.3.6) + pg: 8.20.0 + pg-query-stream: 4.14.0(pg@8.20.0) + pg-types: 4.1.0 + postgres-array: 3.0.4 + zod: 4.3.6 + transitivePeerDependencies: + - pg-native + + '@slonik/sql-tag@48.13.2': + dependencies: + '@slonik/errors': 48.13.2(zod@4.3.6) + '@slonik/types': 48.13.2(zod@4.3.6) + roarr: 7.21.4 + safe-stable-stringify: 2.5.0 + serialize-error: 12.0.0 + zod: 4.3.6 + + '@slonik/types@48.13.2(zod@4.3.6)': + dependencies: + zod: 4.3.6 + + '@slonik/utilities@48.13.2(zod@4.3.6)': + dependencies: + '@slonik/errors': 48.13.2(zod@4.3.6) + '@slonik/types': 48.13.2(zod@4.3.6) + zod: 4.3.6 + '@smithy/abort-controller@4.0.0': dependencies: '@smithy/types': 4.11.0 @@ -31076,7 +31136,7 @@ snapshots: dependencies: '@types/node': 24.10.9 pg-protocol: 1.7.0 - pg-types: 4.0.1 + pg-types: 4.1.0 '@types/pg@8.15.6': dependencies: @@ -32298,8 +32358,6 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 - bufferput@0.1.3: {} - bufrw@1.4.0: dependencies: ansi-color: 0.2.1 @@ -32864,8 +32922,6 @@ snapshots: dependencies: browserslist: 4.28.1 - core-js@3.25.5: {} - core-util-is@1.0.2: {} core-util-is@1.0.3: {} @@ -33237,8 +33293,6 @@ snapshots: escodegen: 2.1.0 esprima: 4.0.1 - delay@4.4.1: {} - delayed-stream@1.0.0: {} delegates@1.0.0: {} @@ -33609,8 +33663,6 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 - es6-error@4.1.1: {} - esast-util-from-estree@2.0.0: dependencies: '@types/estree-jsx': 1.0.0 @@ -34253,13 +34305,6 @@ snapshots: fast-json-stable-stringify@2.1.0: {} - fast-json-stringify@2.7.13: - dependencies: - ajv: 6.14.0 - deepmerge: 4.3.1 - rfdc: 1.4.1 - string-similarity: 4.0.4 - fast-json-stringify@5.16.1: dependencies: '@fastify/merge-json-schemas': 0.1.1 @@ -34690,11 +34735,6 @@ snapshots: data-uri-to-buffer: 2.0.2 source-map: 0.6.1 - get-stack-trace@2.1.1: - dependencies: - bluebird: 3.7.2 - source-map: 0.8.0-beta.0 - get-stdin@8.0.0: {} get-stdin@9.0.0: {} @@ -35589,11 +35629,6 @@ snapshots: humps@2.0.1: {} - hyperid@2.3.1: - dependencies: - uuid: 8.3.2 - uuid-parse: 1.1.0 - hyperid@3.3.0: dependencies: buffer: 5.7.1 @@ -35734,8 +35769,6 @@ snapshots: inline-style-parser@0.2.4: {} - int64-buffer@0.99.1007: {} - internal-slot@1.1.0: dependencies: es-errors: 1.3.0 @@ -35962,8 +35995,6 @@ snapshots: dependencies: isobject: 3.0.1 - is-plain-object@5.0.0: {} - is-primitive@3.0.1: {} is-property@1.0.2: {} @@ -36092,7 +36123,7 @@ snapshots: isexe@3.1.1: {} - iso8601-duration@1.3.0: {} + iso8601-duration@2.1.3: {} isobject@3.0.1: {} @@ -37839,8 +37870,6 @@ snapshots: muggle-string@0.4.1: {} - multi-fork@0.0.2: {} - mustache@4.2.0: {} mute-stream@1.0.0: {} @@ -38237,8 +38266,6 @@ snapshots: p-cancelable@4.0.1(patch_hash=195ae63d47810ca4c987421948f15869356e598dccde9e8c9a4ff4efd3e88322): {} - p-defer@3.0.0: {} - p-filter@2.1.0: dependencies: p-map: 2.1.0 @@ -38410,7 +38437,7 @@ snapshots: lines-and-columns: 2.0.3 type-fest: 3.13.1 - parse-ms@2.1.0: {} + parse-ms@4.0.0: {} parse-node-version@1.0.1: optional: true @@ -38500,24 +38527,16 @@ snapshots: pg-cloudflare@1.1.1: optional: true + pg-cloudflare@1.3.0: + optional: true + + pg-connection-string@2.12.0: {} + pg-connection-string@2.9.1: {} - pg-copy-streams-binary@2.2.0: + pg-cursor@2.19.0(pg@8.20.0): dependencies: - bl: 4.1.0 - bufferput: 0.1.3 - ieee754: 1.2.1 - int64-buffer: 0.99.1007 - multi-fork: 0.0.2 - through2: 3.0.2 - - pg-copy-streams@6.0.4: - dependencies: - obuf: 1.1.2 - - pg-cursor@2.18.0(pg@8.13.1): - dependencies: - pg: 8.13.1 + pg: 8.20.0 pg-int8@1.0.1: {} @@ -38525,26 +38544,32 @@ snapshots: pg-numeric@1.0.2: {} + pg-pool@3.13.0(pg@8.20.0): + dependencies: + pg: 8.20.0 + pg-pool@3.7.0(pg@8.13.1): dependencies: pg: 8.13.1 - pg-promise@11.10.2(pg-query-stream@4.7.0(pg@8.13.1)): + pg-promise@11.10.2(pg-query-stream@4.14.0(pg@8.20.0)): dependencies: assert-options: 0.8.2 pg: 8.13.1 pg-minify: 1.6.5 - pg-query-stream: 4.7.0(pg@8.13.1) + pg-query-stream: 4.14.0(pg@8.20.0) spex: 3.4.0 transitivePeerDependencies: - pg-native + pg-protocol@1.13.0: {} + pg-protocol@1.7.0: {} - pg-query-stream@4.7.0(pg@8.13.1): + pg-query-stream@4.14.0(pg@8.20.0): dependencies: - pg: 8.13.1 - pg-cursor: 2.18.0(pg@8.13.1) + pg: 8.20.0 + pg-cursor: 2.19.0(pg@8.20.0) pg-types@2.2.0: dependencies: @@ -38554,13 +38579,13 @@ snapshots: postgres-date: 1.0.7 postgres-interval: 1.2.0 - pg-types@4.0.1: + pg-types@4.1.0: dependencies: pg-int8: 1.0.1 pg-numeric: 1.0.2 - postgres-array: 3.0.1 + postgres-array: 3.0.4 postgres-bytea: 3.0.0 - postgres-date: 2.0.1 + postgres-date: 2.1.0 postgres-interval: 3.0.0 postgres-range: 1.1.3 @@ -38574,6 +38599,16 @@ snapshots: optionalDependencies: pg-cloudflare: 1.1.1 + pg@8.20.0: + dependencies: + pg-connection-string: 2.12.0 + pg-pool: 3.13.0(pg@8.20.0) + pg-protocol: 1.13.0 + pg-types: 2.2.0 + pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.3.0 + pgpass@1.0.5: dependencies: split2: 4.1.0 @@ -38708,7 +38743,7 @@ snapshots: postgres-array@2.0.0: {} - postgres-array@3.0.1: {} + postgres-array@3.0.4: {} postgres-bytea@1.0.0: {} @@ -38718,7 +38753,7 @@ snapshots: postgres-date@1.0.7: {} - postgres-date@2.0.1: {} + postgres-date@2.1.0: {} postgres-interval@1.2.0: dependencies: @@ -38726,7 +38761,7 @@ snapshots: postgres-interval@3.0.0: {} - postgres-interval@4.0.0: {} + postgres-interval@4.0.2: {} postgres-range@1.1.3: {} @@ -38770,9 +38805,9 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.3.1 - pretty-ms@7.0.1: + pretty-ms@9.3.0: dependencies: - parse-ms: 2.1.0 + parse-ms: 4.0.0 pretty-quick@4.0.0(prettier@3.4.2): dependencies: @@ -39552,12 +39587,9 @@ snapshots: glob: 13.0.6 package-json-from-dist: 1.0.1 - roarr@7.14.3: + roarr@7.21.4: dependencies: - boolean: 3.2.0 - fast-json-stringify: 2.7.13 fast-printf: 1.6.9 - globalthis: 1.0.4 safe-stable-stringify: 2.5.0 semver-compare: 1.0.0 @@ -39766,13 +39798,9 @@ snapshots: tslib: 2.8.1 upper-case-first: 2.0.2 - serialize-error@5.0.0: + serialize-error@12.0.0: dependencies: - type-fest: 0.8.1 - - serialize-error@8.1.0: - dependencies: - type-fest: 0.20.2 + type-fest: 4.41.0 seroval-plugins@1.3.3(seroval@1.5.0): dependencies: @@ -39965,44 +39993,35 @@ snapshots: slick@1.12.2: {} - slonik-interceptor-query-logging@46.4.0(slonik@30.4.4(patch_hash=195b140c0181c27a85a6026c0058087a419e38f6c5d89f5f2c608e39f5bf23e9)): + slonik-interceptor-query-logging@48.13.2(slonik@48.13.2(patch_hash=e16e74ebc8c5f42be257d540b5ac7d175ff3264481046b5c33fc647c9997acdc)): dependencies: crack-json: 1.3.0 - pretty-ms: 7.0.1 - serialize-error: 8.1.0 - slonik: 30.4.4(patch_hash=195b140c0181c27a85a6026c0058087a419e38f6c5d89f5f2c608e39f5bf23e9) + pretty-ms: 9.3.0 + serialize-error: 12.0.0 + slonik: 48.13.2(patch_hash=e16e74ebc8c5f42be257d540b5ac7d175ff3264481046b5c33fc647c9997acdc) - slonik-utilities@1.9.4: + slonik-sql-tag-raw@48.13.2(slonik@48.13.2(patch_hash=e16e74ebc8c5f42be257d540b5ac7d175ff3264481046b5c33fc647c9997acdc)): dependencies: - core-js: 3.25.5 - delay: 4.4.1 - es6-error: 4.1.1 - lodash: 4.18.1 - roarr: 7.14.3 - serialize-error: 5.0.0 + '@slonik/sql-tag': 48.13.2 + roarr: 7.21.4 + slonik: 48.13.2(patch_hash=e16e74ebc8c5f42be257d540b5ac7d175ff3264481046b5c33fc647c9997acdc) - slonik@30.4.4(patch_hash=195b140c0181c27a85a6026c0058087a419e38f6c5d89f5f2c608e39f5bf23e9): + slonik@48.13.2(patch_hash=e16e74ebc8c5f42be257d540b5ac7d175ff3264481046b5c33fc647c9997acdc): dependencies: - concat-stream: 2.0.0 - es6-error: 4.1.1 - fast-safe-stringify: 2.1.1 - get-stack-trace: 2.1.1 - hyperid: 2.3.1 - is-plain-object: 5.0.0 - iso8601-duration: 1.3.0 - p-defer: 3.0.0 - pg: 8.13.1 - pg-copy-streams: 6.0.4 - pg-copy-streams-binary: 2.2.0 - pg-cursor: 2.18.0(pg@8.13.1) - pg-protocol: 1.7.0 - pg-types: 4.0.1 - postgres-array: 3.0.1 - postgres-interval: 4.0.0 - roarr: 7.14.3 - serialize-error: 8.1.0 - through2: 4.0.2 - zod: 3.25.76 + '@opentelemetry/api': 1.9.0 + '@slonik/driver': 48.13.2(zod@4.3.6) + '@slonik/errors': 48.13.2(zod@4.3.6) + '@slonik/pg-driver': 48.13.2(patch_hash=d281baceb3dc04c2d6348d8b2bb1f5598bef4e24a6a173fc0048c2b3ab6e27ea)(zod@4.3.6) + '@slonik/sql-tag': 48.13.2 + '@slonik/utilities': 48.13.2(zod@4.3.6) + '@standard-schema/spec': 1.0.0 + iso8601-duration: 2.1.3 + p-limit: 6.2.0 + postgres-interval: 4.0.2 + roarr: 7.21.4 + serialize-error: 12.0.0 + strict-event-emitter-types: 2.0.0 + zod: 4.3.6 transitivePeerDependencies: - pg-native @@ -40184,14 +40203,14 @@ snapshots: streamsearch@1.1.0: {} + strict-event-emitter-types@2.0.0: {} + strict-event-emitter@0.5.1: {} string-argv@0.3.2: {} string-env-interpolation@1.0.1: {} - string-similarity@4.0.4: {} - string-template@0.2.1: {} string-width@4.2.3: @@ -40522,15 +40541,6 @@ snapshots: readable-stream: 2.3.8 xtend: 4.0.2 - through2@3.0.2: - dependencies: - inherits: 2.0.4 - readable-stream: 3.6.0 - - through2@4.0.2: - dependencies: - readable-stream: 3.6.0 - through@2.3.8: {} timeout-signal@2.0.0: {} @@ -40837,6 +40847,8 @@ snapshots: type-fest@4.26.1: {} + type-fest@4.41.0: {} + type-fest@5.4.2: dependencies: tagged-tag: 1.0.0