Merge commit '36c66dd290d3ce6eb1ccd310d0c658d4a32bb8eb' as 'repos/effect'

This commit is contained in:
-Puter
2026-07-19 03:25:10 +05:30
parent dd1071cc10
commit 2daf979036
2214 changed files with 673090 additions and 0 deletions

View File

@@ -0,0 +1,382 @@
import { PgClient } from "@effect/sql-pg"
import { assert, expect, it } from "@effect/vitest"
import { Effect, Fiber, Redacted, Stream, String } from "effect"
import { TestClock } from "effect/testing"
import { SqlClient } from "effect/unstable/sql"
import * as Statement from "effect/unstable/sql/Statement"
import { parse as parsePgConnectionString } from "pg-connection-string"
import { PgContainer } from "./utils.ts"
const compilerTransform = PgClient.makeCompiler(String.camelToSnake)
const transformsNested = Statement.defaultTransforms(String.snakeToCamel)
const transforms = Statement.defaultTransforms(String.snakeToCamel, false)
it.layer(PgContainer.layerClient, { timeout: "30 seconds" })("PgClient", (it) => {
it.effect("insert helper", () =>
Effect.gen(function*() {
const sql = yield* PgClient.PgClient
const [query, params] = sql`INSERT INTO people ${sql.insert({ name: "Tim", age: 10 })}`.compile()
expect(query).toEqual(`INSERT INTO people ("name","age") VALUES ($1,$2)`)
expect(params).toEqual(["Tim", 10])
}))
it.effect("updateValues helper", () =>
Effect.gen(function*() {
const sql = yield* PgClient.PgClient
const [query, params] = sql`UPDATE people SET name = data.name FROM ${
sql.updateValues(
[{ name: "Tim" }, { name: "John" }],
"data"
)
}`.compile()
expect(query).toEqual(
`UPDATE people SET name = data.name FROM (values ($1),($2)) AS data("name")`
)
expect(params).toEqual(["Tim", "John"])
}))
it.effect("updateValues helper returning", () =>
Effect.gen(function*() {
const sql = yield* PgClient.PgClient
const [query, params] = sql`UPDATE people SET name = data.name FROM ${
sql.updateValues(
[{ name: "Tim" }, { name: "John" }],
"data"
).returning("*")
}`.compile()
expect(query).toEqual(
`UPDATE people SET name = data.name FROM (values ($1),($2)) AS data("name") RETURNING *`
)
expect(params).toEqual(["Tim", "John"])
}))
it.effect("update helper", () =>
Effect.gen(function*() {
const sql = yield* PgClient.PgClient
let result = sql`UPDATE people SET ${sql.update({ name: "Tim" })}`.compile()
expect(result[0]).toEqual(`UPDATE people SET "name" = $1`)
expect(result[1]).toEqual(["Tim"])
result = sql`UPDATE people SET ${sql.update({ name: "Tim", age: 10 }, ["age"])}`.compile()
expect(result[0]).toEqual(`UPDATE people SET "name" = $1`)
expect(result[1]).toEqual(["Tim"])
}))
it.effect("update helper returning", () =>
Effect.gen(function*() {
const sql = yield* PgClient.PgClient
const result = sql`UPDATE people SET ${sql.update({ name: "Tim" }).returning("*")}`.compile()
expect(result[0]).toEqual(`UPDATE people SET "name" = $1 RETURNING *`)
expect(result[1]).toEqual(["Tim"])
}))
it.effect("array helper", () =>
Effect.gen(function*() {
const sql = yield* PgClient.PgClient
const [query, params] = sql`SELECT * FROM ${sql("people")} WHERE id IN ${sql.in([1, 2, "string"])}`.compile()
expect(query).toEqual(`SELECT * FROM "people" WHERE id IN ($1,$2,$3)`)
expect(params).toEqual([1, 2, "string"])
}))
it.effect("array helper with column", () =>
Effect.gen(function*() {
const sql = yield* PgClient.PgClient
let result = sql`SELECT * FROM ${sql("people")} WHERE ${sql.in("id", [1, 2, "string"])}`.compile()
expect(result[0]).toEqual(`SELECT * FROM "people" WHERE "id" IN ($1,$2,$3)`)
expect(result[1]).toEqual([1, 2, "string"])
result = sql`SELECT * FROM ${sql("people")} WHERE ${sql.in("id", [])}`.compile()
expect(result[0]).toEqual(`SELECT * FROM "people" WHERE 1=0`)
expect(result[1]).toEqual([])
}))
it.effect("and", () =>
Effect.gen(function*() {
const sql = yield* PgClient.PgClient
const now = new Date()
const result = sql`SELECT * FROM ${sql("people")} WHERE ${
sql.and([
sql.in("name", ["Tim", "John"]),
sql`created_at < ${now}`
])
}`.compile()
expect(result[0]).toEqual(`SELECT * FROM "people" WHERE ("name" IN ($1,$2) AND created_at < $3)`)
expect(result[1]).toEqual(["Tim", "John", now])
}))
it("transform nested", () => {
assert.deepEqual(
transformsNested.array([
{
a_key: 1,
nested: [{ b_key: 2 }],
arr_primitive: [1, "2", true]
}
]) as any,
[
{
aKey: 1,
nested: [{ bKey: 2 }],
arrPrimitive: [1, "2", true]
}
]
)
})
it("transform non nested", () => {
assert.deepEqual(
transforms.array([
{
a_key: 1,
nested: [{ b_key: 2 }],
arr_primitive: [1, "2", true]
}
]) as any,
[
{
aKey: 1,
nested: [{ b_key: 2 }],
arrPrimitive: [1, "2", true]
}
]
)
assert.deepEqual(
transforms.array([
{
json_field: {
test_value: [1, true, null, "text"],
test_nested: {
test_value: [1, true, null, "text"]
}
}
}
]) as any,
[
{
jsonField: {
test_value: [1, true, null, "text"],
test_nested: {
test_value: [1, true, null, "text"]
}
}
}
]
)
})
it.effect("insert fragments", () =>
Effect.gen(function*() {
const sql = yield* PgClient.PgClient
const [query, params] = sql`INSERT INTO people ${
sql.insert({
name: "Tim",
age: 10,
json: sql.json({ a: 1 })
})
}`.compile()
assert.strictEqual(
query,
"INSERT INTO people (\"name\",\"age\",\"json\") VALUES ($1,$2,$3)"
)
assert.lengthOf(params, 3)
}))
it.effect("update fragments", () =>
Effect.gen(function*() {
const sql = yield* PgClient.PgClient
const now = new Date()
const [query, params] = sql`UPDATE people SET json = data.json FROM ${
sql.updateValues(
[{ json: sql.json({ a: 1 }) }, { json: sql.json({ b: 1 }) }],
"data"
)
} WHERE created_at > ${now}`.compile()
assert.strictEqual(
query,
`UPDATE people SET json = data.json FROM (values ($1),($2)) AS data("json") WHERE created_at > $3`
)
assert.lengthOf(params, 3)
}))
it.effect("onDialect", () =>
Effect.gen(function*() {
const sql = yield* PgClient.PgClient
assert.strictEqual(
sql.onDialect({
sqlite: () => "A",
pg: () => "B",
mysql: () => "C",
mssql: () => "D",
clickhouse: () => "E"
}),
"B"
)
assert.strictEqual(
sql.onDialectOrElse({
orElse: () => "A",
pg: () => "B"
}),
"B"
)
}))
it.effect("identifier transform", () =>
Effect.gen(function*() {
const sql = yield* PgClient.PgClient
const [query] = compilerTransform.compile(
sql`SELECT * from ${sql("peopleTest")}`,
false
)
expect(query).toEqual(`SELECT * from "people_test"`)
}))
it.effect("jsonb", () =>
Effect.gen(function*() {
const sql = yield* PgClient.PgClient
const rows = yield* sql<{ json: unknown }>`select ${{ testValue: 123 }}::jsonb as json`
expect(rows[0].json).toEqual({ testValue: 123 })
}))
it.effect("stream", () =>
Effect.gen(function*() {
const sql = yield* SqlClient.SqlClient
const rows = yield* sql`SELECT generate_series(1, 3)`.stream.pipe(
Stream.runCollect
)
expect(rows).toEqual([
{ "generate_series": 1 },
{ "generate_series": 2 },
{ "generate_series": 3 }
])
}))
})
it.layer(PgContainer.layerMakeClient, { timeout: "30 seconds" })("PgClient.makeClient", (it) => {
it.effect("connects before executing queries", () =>
Effect.gen(function*() {
const sql = yield* PgClient.PgClient
const rows = yield* sql<{ value: number }>`SELECT 1 AS value`
assert.deepStrictEqual(rows, [{ value: 1 }])
}))
})
it.layer(PgContainer.layerClientWithTransforms, { timeout: "30 seconds" })("PgClient transforms", (it) => {
it.effect("insert helper", () =>
Effect.gen(function*() {
const sql = yield* PgClient.PgClient
const [query, params] = sql`INSERT INTO people ${sql.insert({ firstName: "Tim", age: 10 })}`.compile()
expect(query).toEqual(`INSERT INTO people ("first_name","age") VALUES ($1,$2)`)
expect(params).toEqual(["Tim", 10])
}))
it.effect("insert helper withoutTransforms", () =>
Effect.gen(function*() {
const sql = (yield* PgClient.PgClient).withoutTransforms()
const [query, params] = sql`INSERT INTO people ${sql.insert({ first_name: "Tim", age: 10 })}`.compile()
expect(query).toEqual(`INSERT INTO people ("first_name","age") VALUES ($1,$2)`)
expect(params).toEqual(["Tim", 10])
}))
it.effect("multi-statement queries", () =>
Effect.gen(function*() {
const sql = yield* SqlClient.SqlClient
const result = yield* sql<{ id: string; name: string }>`
CREATE TABLE test_multi (id TEXT PRIMARY KEY, name TEXT);
INSERT INTO test_multi (id, name) VALUES ('id1', 'test1') RETURNING *;
INSERT INTO test_multi (id, name) VALUES ('id2', 'test2') RETURNING *;
`
expect(result).toHaveLength(3)
expect(result[0]).toEqual([])
expect(result[1]).toEqual([{ id: "id1", name: "test1" }])
expect(result[2]).toEqual([{ id: "id2", name: "test2" }])
}))
it.effect("interruption", () =>
Effect.gen(function*() {
const sql = yield* SqlClient.SqlClient
const conn = yield* sql.reserve
yield* conn.executeRaw("select pg_sleep(1000)", []).pipe(
Effect.timeoutOption("50 millis"),
TestClock.withLive
)
const value = yield* conn.executeValues("select 1", [])
expect(value).toEqual([[1]])
}))
it.effect("Should populate config", () =>
Effect.gen(function*() {
const sql = yield* PgClient.PgClient
assert.isDefined(sql.config.url)
const parsedConfig = parsePgConnectionString(Redacted.value(sql.config.url))
expect(sql.config.host).toEqual(parsedConfig.host)
assert.isNotNull(parsedConfig.port)
assert.isDefined(parsedConfig.port)
expect(sql.config.port).toEqual(parseInt(parsedConfig.port))
expect(sql.config.username).toEqual(parsedConfig.user)
assert.isDefined(sql.config.password)
expect(Redacted.value(sql.config.password)).toEqual(parsedConfig.password)
expect(sql.config.database).toEqual(parsedConfig.database)
}))
})
it.layer(PgContainer.layerClientSingleConnection, { timeout: "30 seconds" })("PgClient listen", (it) => {
it.effect("listen does not reserve a pool connection", () =>
Effect.gen(function*() {
const sql = yield* PgClient.PgClient
const channel = "pool_connection_listen"
const listenFiber = yield* sql.listen(channel).pipe(
Stream.take(1),
Stream.runCollect,
Effect.forkScoped
)
yield* Effect.sleep("250 millis")
const rows = yield* sql<{ value: number }>`SELECT 1 as value`.pipe(
Effect.timeoutOrElse({
duration: "3 seconds",
orElse: () => Effect.fail(new Error("query timed out while listener was active"))
})
)
expect(rows).toEqual([{ value: 1 }])
yield* sql.notify(channel, "payload")
const payloads = yield* Fiber.join(listenFiber).pipe(
Effect.timeoutOrElse({
duration: "3 seconds",
orElse: () => Effect.fail(new Error("listener did not receive notification in time"))
})
)
expect(Array.from(payloads)).toEqual(["payload"])
}).pipe(TestClock.withLive), 20_000)
it.effect("notify sends payload", () =>
Effect.gen(function*() {
const sql = yield* PgClient.PgClient
const channel = "pool_connection_notify"
const listenFiber = yield* sql.listen(channel).pipe(
Stream.take(1),
Stream.runCollect,
Effect.forkScoped
)
yield* Effect.sleep("250 millis")
yield* sql.notify(channel, "payload")
const payloads = yield* Fiber.join(listenFiber).pipe(
Effect.timeoutOrElse({
duration: "3 seconds",
orElse: () => Effect.fail(new Error("listener did not receive notification in time"))
})
)
expect(Array.from(payloads)).toEqual(["payload"])
}).pipe(TestClock.withLive), 20_000)
})

View File

@@ -0,0 +1,16 @@
import { Layer } from "effect"
import * as KeyValueStoreTest from "effect-test/unstable/persistence/KeyValueStoreTest"
import * as KeyValueStore from "effect/unstable/persistence/KeyValueStore"
import { PgContainer } from "./utils.ts"
KeyValueStoreTest.suite(
"sql-pg",
KeyValueStore.layerSql().pipe(Layer.provide(PgContainer.layerClient))
)
KeyValueStoreTest.suite(
"sql-pg-custom-table",
KeyValueStore.layerSql({ table: "effect_key_value_store_custom" }).pipe(
Layer.provide(PgContainer.layerClient)
)
)

View File

@@ -0,0 +1,106 @@
import { assert, it } from "@effect/vitest"
import { Effect, Exit, Fiber, Latch, Layer, Schema } from "effect"
import * as PersistedCacheTest from "effect-test/unstable/persistence/PersistedCacheTest"
import * as PersistedQueueTest from "effect-test/unstable/persistence/PersistedQueueTest"
import { TestClock } from "effect/testing"
import { PersistedQueue, Persistence } from "effect/unstable/persistence"
import { SqlClient } from "effect/unstable/sql"
import { PgContainer } from "./utils.ts"
PersistedCacheTest.suite(
"sql-pg-multi",
Persistence.layerSqlMultiTable.pipe(Layer.provide(PgContainer.layerClient))
)
PersistedCacheTest.suite(
"sql-pg-single",
Persistence.layerSql.pipe(Layer.provide(PgContainer.layerClient))
)
PersistedQueueTest.suite(
"sql-pg",
PersistedQueue.layerStoreSql().pipe(Layer.provide(PgContainer.layerClient))
)
it.layer(PgContainer.layerClient, { timeout: "30 seconds" })("PersistedQueue SQL locks", (it) => {
it.effect("refreshes locks for acquired elements", () =>
Effect.gen(function*() {
const options = {
tableName: "effect_queue_lock_refresh",
pollInterval: "10 millis",
lockRefreshInterval: "100 millis",
lockExpiration: "1 second"
} as const
const store1 = yield* PersistedQueue.makeStoreSql(options)
const store2 = yield* PersistedQueue.makeStoreSql(options)
const element = { message: "hello" }
yield* store1.offer({
name: "lock-refresh",
id: crypto.randomUUID(),
element,
isCustomId: false
})
const acquired = Latch.makeUnsafe()
const first = yield* Effect.scoped(Effect.gen(function*() {
yield* store1.take({ name: "lock-refresh", maxAttempts: 10 })
yield* acquired.open
return yield* Effect.never
})).pipe(Effect.forkScoped)
yield* acquired.await
const second = yield* Effect.scoped(
store2.take({ name: "lock-refresh", maxAttempts: 10 })
).pipe(Effect.forkScoped)
yield* Effect.sleep("1500 millis")
assert.isUndefined(second.pollUnsafe())
yield* Fiber.interrupt(first)
const received = yield* Fiber.join(second)
assert.deepStrictEqual(received.element, element)
}).pipe(TestClock.withLive))
it.effect("counts malformed JSON as an attempt and continues", () =>
Effect.gen(function*() {
const tableName = "effect_queue_invalid_json"
const store = yield* PersistedQueue.makeStoreSql({
tableName,
pollInterval: "10 millis"
})
const factory = yield* PersistedQueue.makeFactory.pipe(
Effect.provideService(PersistedQueue.PersistedQueueStore, store)
)
const queue = yield* factory.make({
name: "invalid-json",
schema: Schema.String
})
const sql = (yield* SqlClient.SqlClient).withoutTransforms()
const table = sql(tableName)
const poisonId = crypto.randomUUID()
yield* store.offer({
name: "invalid-json",
id: poisonId,
element: "poison",
isCustomId: false
})
yield* sql`UPDATE ${table} SET element = ${"{"} WHERE id = ${poisonId}`
yield* queue.offer("valid")
const malformed = yield* Effect.exit(queue.take(Effect.succeed, { maxAttempts: 1 }))
assert.isTrue(Exit.isFailure(malformed))
const rows = yield* sql<{
readonly attempts: number
readonly last_failure: string | null
}>`SELECT attempts, last_failure FROM ${table} WHERE id = ${poisonId}`
assert.strictEqual(rows[0].attempts, 1)
assert.isNotNull(rows[0].last_failure)
const value = yield* queue.take(Effect.succeed, { maxAttempts: 1 })
assert.strictEqual(value, "valid")
}).pipe(TestClock.withLive))
})

View File

@@ -0,0 +1,81 @@
import { PgClient } from "@effect/sql-pg"
import { assert, describe, it } from "@effect/vitest"
import { Effect } from "effect"
import * as Reactivity from "effect/unstable/reactivity/Reactivity"
import type * as SqlError from "effect/unstable/sql/SqlError"
const queryFailureReason = (cause: unknown) =>
Effect.gen(function*() {
const client = yield* PgClient.fromPool({
acquire: Effect.succeed(makeFailingPool(cause) as any)
})
const error = yield* Effect.flip(client`SELECT 1`)
return error.reason
}).pipe(
Effect.scoped,
Effect.provide(Reactivity.layer)
)
const queryFailureReasonTag = (cause: unknown) => Effect.map(queryFailureReason(cause), (reason) => reason._tag)
const assertUniqueViolation = (reason: SqlError.SqlErrorReason, constraint: string) => {
assert.strictEqual(reason._tag, "UniqueViolation")
if (reason._tag === "UniqueViolation") {
assert.strictEqual(reason.constraint, constraint)
}
}
const makeFailingPool = (cause: unknown) => ({
options: {},
ending: false,
connect: (cb: (cause: unknown, client: any) => void) => cb(null, makeFailingClient(cause)),
query: () => undefined
})
const makeFailingClient = (cause: unknown) => ({
once: () => undefined,
off: () => undefined,
release: () => undefined,
query: (_sql: string, _params: ReadonlyArray<unknown>, cb: (cause: unknown) => void) => cb(cause)
})
describe("PgClient SqlError classification", () => {
it.effect("checks 42501 before generic 42*", () =>
Effect.gen(function*() {
const authorizationTag = yield* queryFailureReasonTag({ code: "42501" })
assert.strictEqual(authorizationTag, "AuthorizationError")
const syntaxTag = yield* queryFailureReasonTag({ code: "42P01" })
assert.strictEqual(syntaxTag, "SqlSyntaxError")
}))
it.effect("falls back to UnknownError for unmapped SQLSTATE", () =>
Effect.gen(function*() {
const tag = yield* queryFailureReasonTag({ code: "ZZZZZ" })
assert.strictEqual(tag, "UnknownError")
}))
it.effect("classifies 23505 as UniqueViolation and trims the constraint name", () =>
Effect.gen(function*() {
const reason = yield* queryFailureReason({ code: "23505", constraint: " users_email_key " })
assertUniqueViolation(reason, "users_email_key")
}))
it.effect("uses unknown for missing, non-string, or blank unique violation constraints", () =>
Effect.gen(function*() {
const missing = yield* queryFailureReason({ code: "23505" })
assertUniqueViolation(missing, "unknown")
const nonString = yield* queryFailureReason({ code: "23505", constraint: 123 })
assertUniqueViolation(nonString, "unknown")
const blank = yield* queryFailureReason({ code: "23505", constraint: " " })
assertUniqueViolation(blank, "unknown")
}))
it.effect("keeps non-unique integrity constraints classified as ConstraintError", () =>
Effect.gen(function*() {
const tag = yield* queryFailureReasonTag({ code: "23503", constraint: "orders_user_id_fkey" })
assert.strictEqual(tag, "ConstraintError")
}))
})

View File

@@ -0,0 +1,7 @@
import * as SqlEventLogServerUnencryptedStorageTest from "effect-test/unstable/eventlog/SqlEventLogServerUnencryptedStorageTest"
import { PgContainer } from "./utils.ts"
SqlEventLogServerUnencryptedStorageTest.suite(
"sql-pg",
PgContainer.layerClient
)

View File

@@ -0,0 +1,52 @@
import { PgClient } from "@effect/sql-pg"
import { assert, it } from "@effect/vitest"
import { Effect } from "effect"
import * as Reactivity from "effect/unstable/reactivity/Reactivity"
type ConnectCb = (cause: unknown, client?: unknown, release?: (cause?: Error) => void) => void
const makeFailingPool = (cause: unknown) => ({
options: {},
ending: false,
connect: (cb: ConnectCb) => cb(cause, undefined, () => undefined),
query: () => undefined
})
const makeMissingClientPool = () => ({
options: {},
ending: false,
connect: (cb: ConnectCb) => cb(null, undefined, () => undefined),
query: () => undefined
})
it.effect("withTransaction surfaces acquire failures instead of defecting", () =>
Effect.gen(function*() {
const sql = yield* PgClient.fromPool({
acquire: Effect.succeed(makeFailingPool({ code: "08006" }) as any)
})
const error = yield* Effect.flip(sql.withTransaction(sql`SELECT 1`))
assert.strictEqual(error.reason._tag, "ConnectionError")
assert.strictEqual(error.reason.message, "Failed to acquire connection for transaction")
assert.strictEqual(error.reason.operation, "acquireConnection")
}).pipe(
Effect.scoped,
Effect.provide(Reactivity.layer)
))
it.effect("withTransaction surfaces missing-client acquire as ConnectionError", () =>
Effect.gen(function*() {
const sql = yield* PgClient.fromPool({
acquire: Effect.succeed(makeMissingClientPool() as any)
})
const error = yield* Effect.flip(sql.withTransaction(sql`SELECT 1`))
assert.strictEqual(error.reason._tag, "ConnectionError")
assert.strictEqual(error.reason.message, "Failed to acquire connection for transaction")
assert.strictEqual(error.reason.operation, "acquireConnection")
}).pipe(
Effect.scoped,
Effect.provide(Reactivity.layer)
))

View File

@@ -0,0 +1,58 @@
import { PgClient } from "@effect/sql-pg"
import { PostgreSqlContainer } from "@testcontainers/postgresql"
import { Context, Data, Effect, Layer, Redacted, String } from "effect"
export class ContainerError extends Data.TaggedError("ContainerError")<{
cause: unknown
}> {}
export class PgContainer extends Context.Service<PgContainer>()("test/PgContainer", {
make: Effect.acquireRelease(
Effect.tryPromise({
try: () => new PostgreSqlContainer("postgres:alpine").start(),
catch: (cause) => new ContainerError({ cause })
}),
(container) => Effect.promise(() => container.stop())
)
}) {
static readonly layer = Layer.effect(this)(this.make)
static layerClient = Layer.unwrap(
Effect.gen(function*() {
const container = yield* PgContainer
return PgClient.layer({
url: Redacted.make(container.getConnectionUri())
})
})
).pipe(Layer.provide(this.layer))
static layerMakeClient = Layer.unwrap(
Effect.gen(function*() {
const container = yield* PgContainer
return PgClient.layerFrom(PgClient.makeClient({
url: Redacted.make(container.getConnectionUri())
}))
})
).pipe(Layer.provide(this.layer))
static layerClientWithTransforms = Layer.unwrap(
Effect.gen(function*() {
const container = yield* PgContainer
return PgClient.layer({
url: Redacted.make(container.getConnectionUri()),
transformResultNames: String.snakeToCamel,
transformQueryNames: String.camelToSnake
})
})
).pipe(Layer.provide(this.layer))
static layerClientSingleConnection = Layer.unwrap(
Effect.gen(function*() {
const container = yield* PgContainer
return PgClient.layer({
url: Redacted.make(container.getConnectionUri()),
maxConnections: 1
})
})
).pipe(Layer.provide(this.layer))
}