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,106 @@
import { LibsqlClient } from "@effect/sql-libsql"
import { assert, describe, layer } from "@effect/vitest"
import { Effect, Layer } from "effect"
import { LibsqlContainer } from "./util.ts"
const Migrations = Layer.effectDiscard(
LibsqlClient.LibsqlClient.pipe(
Effect.andThen((sql) =>
Effect.acquireRelease(
sql`CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)`,
() => sql`DROP TABLE test;`.pipe(Effect.ignore)
)
)
)
)
describe("Client", () => {
layer(LibsqlContainer.layerClient, { timeout: "30 seconds" })((it) => {
it.effect("should work", () =>
Effect.gen(function*() {
const sql = yield* LibsqlClient.LibsqlClient
let response = yield* sql`INSERT INTO test (name) VALUES ('hello')`
assert.deepStrictEqual(response, [])
response = yield* sql`SELECT * FROM test`
assert.deepStrictEqual(response, [{ id: 1, name: "hello" }])
response = yield* sql`SELECT * FROM test`
assert.deepStrictEqual(yield* sql`select * from test`.values, [
[1, "hello"]
])
}).pipe(Effect.provide(Migrations)))
it.effect("should work with raw", () =>
Effect.gen(function*() {
const sql = yield* LibsqlClient.LibsqlClient
let response: any
response = yield* sql`CREATE TABLE test2 (id INTEGER PRIMARY KEY, name TEXT)`.raw
yield* Effect.addFinalizer(() => sql`DROP TABLE test2;`.pipe(Effect.ignore))
assert.deepStrictEqual(response.toJSON(), {
columnTypes: [],
columns: [],
lastInsertRowid: null,
rows: [],
rowsAffected: 0
})
response = yield* sql`INSERT INTO test (name) VALUES ('hello')`.raw
assert.deepStrictEqual(response.toJSON(), {
columnTypes: [],
columns: [],
lastInsertRowid: "1",
rows: [],
rowsAffected: 1
})
response = yield* sql`SELECT * FROM test`.raw
assert.deepStrictEqual(response.toJSON(), {
columnTypes: ["INTEGER", "TEXT"],
columns: ["id", "name"],
lastInsertRowid: null,
rows: [[1, "hello"]],
rowsAffected: 0
})
}).pipe(Effect.provide(Migrations)))
it.effect("withTransaction", () =>
Effect.gen(function*() {
const sql = yield* LibsqlClient.LibsqlClient
yield* sql.withTransaction(sql`INSERT INTO test (name) VALUES ('hello')`)
const rows = yield* sql`SELECT * FROM test`
assert.deepStrictEqual(rows, [{ id: 1, name: "hello" }])
}).pipe(Effect.provide(Migrations)))
it.effect("withTransaction rollback", () =>
Effect.gen(function*() {
const sql = yield* LibsqlClient.LibsqlClient
yield* sql`INSERT INTO test (name) VALUES ('hello')`.pipe(
Effect.andThen(Effect.fail("boom")),
sql.withTransaction,
Effect.ignore
)
const rows = yield* sql`SELECT * FROM test`
assert.deepStrictEqual(rows, [])
}).pipe(Effect.provide(Migrations)))
it.effect("withTransaction nested", () =>
Effect.gen(function*() {
const sql = yield* LibsqlClient.LibsqlClient
const stmt = sql`INSERT INTO test (name) VALUES ('hello')`
yield* stmt.pipe(Effect.andThen(() => stmt.pipe(sql.withTransaction)), sql.withTransaction)
const rows = yield* sql<{ total_rows: number }>`select count(*) as total_rows FROM test`
assert.deepStrictEqual(rows.at(0)?.total_rows, 2)
}).pipe(Effect.provide(Migrations)))
it.effect("withTransaction nested rollback", () =>
Effect.gen(function*() {
const sql = yield* LibsqlClient.LibsqlClient
const stmt = sql`INSERT INTO test (name) VALUES ('hello')`
yield* stmt.pipe(
Effect.andThen(() => stmt.pipe(Effect.andThen(Effect.fail("boom")), sql.withTransaction, Effect.ignore)),
sql.withTransaction
)
const rows = yield* sql<{ total_rows: number }>`select count(*) as total_rows FROM test`
assert.deepStrictEqual(rows.at(0)?.total_rows, 1)
}).pipe(Effect.provide(Migrations)))
})
})

View File

@@ -0,0 +1,177 @@
import { LibsqlClient } from "@effect/sql-libsql"
import { assert, describe, layer } from "@effect/vitest"
import { Cause, Effect, Iterable } from "effect"
import * as Schema from "effect/Schema"
import { SqlError, SqlResolver } from "effect/unstable/sql"
import { LibsqlContainer } from "./util.ts"
const seededClient = Effect.gen(function*() {
const sql = yield* LibsqlClient.LibsqlClient
yield* sql`CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)`
for (const id of Iterable.range(1, 100)) {
yield* sql`INSERT INTO test ${sql.insert({ id, name: `name${id}` })}`
}
yield* Effect.addFinalizer(() => sql`DROP TABLE test;`.pipe(Effect.orDie))
return sql
})
layer(LibsqlContainer.layerClient, { timeout: "30 seconds" })("Resolver", (it) => {
describe.sequential("ordered", () => {
it.effect("insert", () =>
Effect.gen(function*() {
const batches: Array<Array<string>> = []
const sql = yield* seededClient
const Insert = SqlResolver.ordered({
Request: Schema.String,
Result: Schema.Struct({ id: Schema.Number, name: Schema.String }),
execute: (names) => {
batches.push(names)
return sql`INSERT INTO test ${sql.insert(names.map((name) => ({ name })))} RETURNING *`
}
})
const execute = SqlResolver.request(Insert)
assert.deepStrictEqual(
yield* Effect.all({
one: execute("one"),
two: execute("two")
}, { concurrency: "unbounded" }),
{
one: { id: 101, name: "one" },
two: { id: 102, name: "two" }
}
)
assert.deepStrictEqual(batches, [["one", "two"]])
}))
it.effect("result length mismatch", () =>
Effect.gen(function*() {
const batches: Array<Array<number>> = []
const sql = yield* seededClient
const Select = SqlResolver.ordered({
Request: Schema.Number,
Result: Schema.Struct({ id: Schema.Number, name: Schema.String }),
execute: (ids) => {
batches.push(ids)
return sql`SELECT * FROM test WHERE id IN ${sql.in(ids)}`
}
})
const execute = SqlResolver.request(Select)
const error = yield* Effect.all([
execute(1),
execute(2),
execute(3),
execute(101)
], { concurrency: "unbounded" }).pipe(
Effect.flip
)
assert(error instanceof SqlError.ResultLengthMismatch)
assert.strictEqual(error.actual, 3)
assert.strictEqual(error.expected, 4)
assert.deepStrictEqual(batches, [[1, 2, 3, 101]])
}))
})
describe.sequential("grouped", () => {
it.effect("find by name", () =>
Effect.gen(function*() {
const sql = yield* seededClient
const FindByName = SqlResolver.grouped({
Request: Schema.String,
RequestGroupKey: (name) => name,
Result: Schema.Struct({ id: Schema.Number, name: Schema.String }),
ResultGroupKey: (result) => result.name,
execute: (names) => sql`SELECT * FROM test WHERE name IN ${sql.in(names)}`
})
yield* sql`INSERT INTO test ${sql.insert({ name: "name1" })}`
const execute = SqlResolver.request(FindByName)
assert.deepStrictEqual(
yield* Effect.all({
one: execute("name1"),
two: execute("name2"),
three: Effect.flip(execute("name0"))
}, { concurrency: "unbounded" }),
{
one: [{ id: 1, name: "name1" }, { id: 101, name: "name1" }],
two: [{ id: 2, name: "name2" }],
three: new Cause.NoSuchElementError()
}
)
}))
it.effect("using raw rows", () =>
Effect.gen(function*() {
const sql = yield* seededClient
const FindByName = SqlResolver.grouped({
Request: Schema.String,
RequestGroupKey: (name) => name,
Result: Schema.Struct({ id: Schema.Number, name: Schema.String }),
ResultGroupKey: (_, result: any) => result.name,
execute: (names) => sql`SELECT * FROM test WHERE name IN ${sql.in(names)}`
})
yield* sql`INSERT INTO test ${sql.insert({ name: "name1" })}`
const execute = SqlResolver.request(FindByName)
assert.deepStrictEqual(
yield* Effect.all({
one: execute("name1"),
two: execute("name2"),
three: Effect.flip(execute("name0"))
}, { concurrency: "unbounded" }),
{
one: [{ id: 1, name: "name1" }, { id: 101, name: "name1" }],
two: [{ id: 2, name: "name2" }],
three: new Cause.NoSuchElementError()
}
)
}))
})
describe.sequential("findById", () => {
it.effect("find by id", () =>
Effect.gen(function*() {
const sql = yield* seededClient
const FindById = SqlResolver.findById({
Id: Schema.Number,
Result: Schema.Struct({ id: Schema.Number, name: Schema.String }),
ResultId: (result) => result.id,
execute: (ids) => sql`SELECT * FROM test WHERE id IN ${sql.in(ids)}`
})
const execute = SqlResolver.request(FindById)
assert.deepStrictEqual(
yield* Effect.all({
one: execute(1),
two: execute(2),
three: Effect.flip(execute(101))
}, { concurrency: "unbounded" }),
{
one: { id: 1, name: "name1" },
two: { id: 2, name: "name2" },
three: new Cause.NoSuchElementError()
}
)
}))
it.effect("using raw rows", () =>
Effect.gen(function*() {
const sql = yield* seededClient
const FindById = SqlResolver.findById({
Id: Schema.Number,
Result: Schema.Struct({ id: Schema.Number, name: Schema.String }),
ResultId: (_, result: any) => result.id,
execute: (ids) => sql`SELECT * FROM test WHERE id IN ${sql.in(ids)}`
})
const execute = SqlResolver.request(FindById)
assert.deepStrictEqual(
yield* Effect.all({
one: execute(1),
two: execute(2),
three: Effect.flip(execute(101))
}, { concurrency: "unbounded" }),
{
one: { id: 1, name: "name1" },
two: { id: 2, name: "name2" },
three: new Cause.NoSuchElementError()
}
)
}))
})
})

View File

@@ -0,0 +1,54 @@
import { LibsqlClient } from "@effect/sql-libsql"
import { Context, Effect, Layer } from "effect"
import { GenericContainer, type StartedTestContainer } from "testcontainers"
const waitForServer = async (container: StartedTestContainer) => {
const url = `http://${container.getHost()}:${container.getMappedPort(8080)}`
let error: unknown = undefined
for (let i = 0; i < 60; i++) {
try {
await fetch(url)
return
} catch (cause) {
error = cause
await new Promise((resolve) => setTimeout(resolve, 250))
}
}
throw error
}
export class LibsqlContainer extends Context.Service<
LibsqlContainer,
StartedTestContainer
>()("test/LibsqlContainer") {
static readonly layer = Layer.effect(this)(
Effect.acquireRelease(
Effect.promise(async () => {
const container = await new GenericContainer("ghcr.io/tursodatabase/libsql-server:main")
.withExposedPorts(8080)
.withEnvironment({ SQLD_NODE: "primary" })
.withCommand(["sqld", "--no-welcome", "--http-listen-addr", "0.0.0.0:8080"]).start()
try {
await waitForServer(container)
return container
} catch (error) {
await container.stop()
throw error
}
}),
(container) => Effect.promise(() => container.stop())
)
)
static layerClient = Layer.unwrap(
Effect.gen(function*() {
const container = yield* LibsqlContainer
return LibsqlClient.layer({
url: `http://${container.getHost()}:${container.getMappedPort(8080)}`
})
})
).pipe(Layer.provide(this.layer))
}