This commit is contained in:
22
bun.lock
22
bun.lock
@@ -30,6 +30,20 @@
|
|||||||
"effect": "^3.19.0",
|
"effect": "^3.19.0",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"packages/example": {
|
||||||
|
"name": "@effect-docker/example",
|
||||||
|
"dependencies": {
|
||||||
|
"@effect/platform": "^0.94.4",
|
||||||
|
"@effect/platform-bun": "^0.87.1",
|
||||||
|
"@effect/platform-node": "^0.104.1",
|
||||||
|
"effect": "^3.19.16",
|
||||||
|
"effect-docker": "workspace:*",
|
||||||
|
"undici": "^7.19.0",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"tsx": "^4.21.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@biomejs/biome": ["@biomejs/biome@2.3.11", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.11", "@biomejs/cli-darwin-x64": "2.3.11", "@biomejs/cli-linux-arm64": "2.3.11", "@biomejs/cli-linux-arm64-musl": "2.3.11", "@biomejs/cli-linux-x64": "2.3.11", "@biomejs/cli-linux-x64-musl": "2.3.11", "@biomejs/cli-win32-arm64": "2.3.11", "@biomejs/cli-win32-x64": "2.3.11" }, "bin": { "biome": "bin/biome" } }, "sha512-/zt+6qazBWguPG6+eWmiELqO+9jRsMZ/DBU3lfuU2ngtIQYzymocHhKiZRyrbra4aCOoyTg/BmY+6WH5mv9xmQ=="],
|
"@biomejs/biome": ["@biomejs/biome@2.3.11", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.11", "@biomejs/cli-darwin-x64": "2.3.11", "@biomejs/cli-linux-arm64": "2.3.11", "@biomejs/cli-linux-arm64-musl": "2.3.11", "@biomejs/cli-linux-x64": "2.3.11", "@biomejs/cli-linux-x64-musl": "2.3.11", "@biomejs/cli-win32-arm64": "2.3.11", "@biomejs/cli-win32-x64": "2.3.11" }, "bin": { "biome": "bin/biome" } }, "sha512-/zt+6qazBWguPG6+eWmiELqO+9jRsMZ/DBU3lfuU2ngtIQYzymocHhKiZRyrbra4aCOoyTg/BmY+6WH5mv9xmQ=="],
|
||||||
@@ -50,13 +64,15 @@
|
|||||||
|
|
||||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.11", "", { "os": "win32", "cpu": "x64" }, "sha512-43VrG813EW+b5+YbDbz31uUsheX+qFKCpXeY9kfdAx+ww3naKxeVkTD9zLIWxUPfJquANMHrmW3wbe/037G0Qg=="],
|
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.11", "", { "os": "win32", "cpu": "x64" }, "sha512-43VrG813EW+b5+YbDbz31uUsheX+qFKCpXeY9kfdAx+ww3naKxeVkTD9zLIWxUPfJquANMHrmW3wbe/037G0Qg=="],
|
||||||
|
|
||||||
|
"@effect-docker/example": ["@effect-docker/example@workspace:packages/example"],
|
||||||
|
|
||||||
"@effect/cluster": ["@effect/cluster@0.56.1", "", { "dependencies": { "kubernetes-types": "^1.30.0" }, "peerDependencies": { "@effect/platform": "^0.94.1", "@effect/rpc": "^0.73.0", "@effect/sql": "^0.49.0", "@effect/workflow": "^0.16.0", "effect": "^3.19.14" } }, "sha512-gnrsH6kfrUjn+82j/bw1IR4yFqJqV8tc7xZvrbJPRgzANycc6K1hu3LMg548uYbUkTzD8YYyqrSatMO1mkQpzw=="],
|
"@effect/cluster": ["@effect/cluster@0.56.1", "", { "dependencies": { "kubernetes-types": "^1.30.0" }, "peerDependencies": { "@effect/platform": "^0.94.1", "@effect/rpc": "^0.73.0", "@effect/sql": "^0.49.0", "@effect/workflow": "^0.16.0", "effect": "^3.19.14" } }, "sha512-gnrsH6kfrUjn+82j/bw1IR4yFqJqV8tc7xZvrbJPRgzANycc6K1hu3LMg548uYbUkTzD8YYyqrSatMO1mkQpzw=="],
|
||||||
|
|
||||||
"@effect/experimental": ["@effect/experimental@0.58.0", "", { "dependencies": { "uuid": "^11.0.3" }, "peerDependencies": { "@effect/platform": "^0.94.0", "effect": "^3.19.13", "ioredis": "^5", "lmdb": "^3" }, "optionalPeers": ["ioredis", "lmdb"] }, "sha512-IEP9sapjF6rFy5TkoqDPc86st/fnqUfjT7Xa3pWJrFGr1hzaMXHo+mWsYOZS9LAOVKnpHuVziDK97EP5qsCHVA=="],
|
"@effect/experimental": ["@effect/experimental@0.58.0", "", { "dependencies": { "uuid": "^11.0.3" }, "peerDependencies": { "@effect/platform": "^0.94.0", "effect": "^3.19.13", "ioredis": "^5", "lmdb": "^3" }, "optionalPeers": ["ioredis", "lmdb"] }, "sha512-IEP9sapjF6rFy5TkoqDPc86st/fnqUfjT7Xa3pWJrFGr1hzaMXHo+mWsYOZS9LAOVKnpHuVziDK97EP5qsCHVA=="],
|
||||||
|
|
||||||
"@effect/language-service": ["@effect/language-service@0.72.0", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-MWkyTPCXSs5Q3OIBWR3q24SA+ipkdWW7EBJBt6EPUzlzZxjJLXtLBhXpMoCFheSEM0FTWOHT4BRLh5lufsmjVw=="],
|
"@effect/language-service": ["@effect/language-service@0.72.0", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-MWkyTPCXSs5Q3OIBWR3q24SA+ipkdWW7EBJBt6EPUzlzZxjJLXtLBhXpMoCFheSEM0FTWOHT4BRLh5lufsmjVw=="],
|
||||||
|
|
||||||
"@effect/platform": ["@effect/platform@0.94.2", "", { "dependencies": { "find-my-way-ts": "^0.1.6", "msgpackr": "^1.11.4", "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^3.19.15" } }, "sha512-85vdwpnK4oH/rJ3EuX/Gi2Hkt+K4HvXWr9bxCuqvty9hxyEcRxkJcqTesYrcVoQB6aULb1Za2B0MKoTbvffB3Q=="],
|
"@effect/platform": ["@effect/platform@0.94.4", "", { "dependencies": { "find-my-way-ts": "^0.1.6", "msgpackr": "^1.11.4", "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^3.19.16" } }, "sha512-mK8pbskFAcBRA5Ooyt02kCBuWltZakyaDcM4ByTY0jXQMFC3NUveNK62JVH7XB+f3XZ8OoBBxUnlLben4plEKQ=="],
|
||||||
|
|
||||||
"@effect/platform-bun": ["@effect/platform-bun@0.87.1", "", { "dependencies": { "@effect/platform-node-shared": "^0.57.1", "multipasta": "^0.2.7" }, "peerDependencies": { "@effect/cluster": "^0.56.1", "@effect/platform": "^0.94.2", "@effect/rpc": "^0.73.0", "@effect/sql": "^0.49.0", "effect": "^3.19.15" } }, "sha512-I88d0YqWbvLY2GGeIxK3r5k0l/MoUCCnxiHJG+X6gqaHu+pIs0djDtJ+ORhw/3qha9ojcVu6pyaBmnUjgzQHWQ=="],
|
"@effect/platform-bun": ["@effect/platform-bun@0.87.1", "", { "dependencies": { "@effect/platform-node-shared": "^0.57.1", "multipasta": "^0.2.7" }, "peerDependencies": { "@effect/cluster": "^0.56.1", "@effect/platform": "^0.94.2", "@effect/rpc": "^0.73.0", "@effect/sql": "^0.49.0", "effect": "^3.19.15" } }, "sha512-I88d0YqWbvLY2GGeIxK3r5k0l/MoUCCnxiHJG+X6gqaHu+pIs0djDtJ+ORhw/3qha9ojcVu6pyaBmnUjgzQHWQ=="],
|
||||||
|
|
||||||
@@ -194,7 +210,7 @@
|
|||||||
|
|
||||||
"devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
|
"devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
|
||||||
|
|
||||||
"effect": ["effect@3.19.15", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-vzMmgfZKLcojmUjBdlQx+uaKryO7yULlRxjpDnHdnvcp1NPHxJyoM6IOXBLlzz2I/uPtZpGKavt5hBv7IvGZkA=="],
|
"effect": ["effect@3.19.16", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-7+XC3vGrbAhCHd8LTFHvnZjRpZKZ8YHRZqJTkpNoxcJ2mCyNs2SwI+6VkV/ij8Y3YW7wfBN4EbU06/F5+m/wkQ=="],
|
||||||
|
|
||||||
"effect-docker": ["effect-docker@workspace:packages/effect-docker"],
|
"effect-docker": ["effect-docker@workspace:packages/effect-docker"],
|
||||||
|
|
||||||
@@ -323,5 +339,7 @@
|
|||||||
"uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="],
|
"uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="],
|
||||||
|
|
||||||
"ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="],
|
"ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="],
|
||||||
|
|
||||||
|
"openapi-to-effect/effect": ["effect@3.19.15", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-vzMmgfZKLcojmUjBdlQx+uaKryO7yULlRxjpDnHdnvcp1NPHxJyoM6IOXBLlzz2I/uPtZpGKavt5hBv7IvGZkA=="],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
4
packages/effect-docker/scripts/tsconfig.json
Normal file
4
packages/effect-docker/scripts/tsconfig.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"include": ["./scripts"]
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { HttpClient, HttpClientRequest } from "@effect/platform"
|
import { HttpClient, HttpClientRequest } from "@effect/platform"
|
||||||
|
import type { ResponseError } from "@effect/platform/HttpClientError"
|
||||||
import { Effect } from "effect"
|
import { Effect } from "effect"
|
||||||
import { type DockerBaseClient, DockerBaseClientImpl, type DockerClientContainerError } from "./DockerBaseClient.js"
|
import { type DockerBaseClient, DockerBaseClientImpl, type DockerClientError } from "./DockerBaseClient.js"
|
||||||
import type { ContainerSummary } from "./gen/v1.53/index.js"
|
import type { ContainerSummary } from "./gen/v1.53/index.js"
|
||||||
|
|
||||||
|
|
||||||
@@ -12,7 +13,18 @@ export type Docker153ClientTypeId = typeof Docker153ClientTypeId
|
|||||||
export interface Docker153Client extends DockerBaseClient {
|
export interface Docker153Client extends DockerBaseClient {
|
||||||
readonly [Docker153ClientTypeId]: Docker153ClientTypeId
|
readonly [Docker153ClientTypeId]: Docker153ClientTypeId
|
||||||
|
|
||||||
listContainers(): Effect.Effect<ContainerSummary, DockerClientContainerError>
|
listContainers(params?: Docker153Client.listContainers.Params): Effect.Effect<readonly ContainerSummary[], DockerClientError>
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare namespace Docker153Client {
|
||||||
|
export namespace listContainers {
|
||||||
|
export interface Params {
|
||||||
|
readonly all?: boolean
|
||||||
|
readonly limit?: number
|
||||||
|
readonly size?: boolean
|
||||||
|
readonly filters?: string
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Docker153ClientImpl
|
export class Docker153ClientImpl
|
||||||
@@ -20,14 +32,21 @@ extends DockerBaseClientImpl
|
|||||||
implements Docker153Client {
|
implements Docker153Client {
|
||||||
readonly [Docker153ClientTypeId]: Docker153ClientTypeId = Docker153ClientTypeId
|
readonly [Docker153ClientTypeId]: Docker153ClientTypeId = Docker153ClientTypeId
|
||||||
|
|
||||||
listContainers() {
|
listContainers(params?: Docker153Client.listContainers.Params) {
|
||||||
return this.httpContainerClient.get("/json").pipe(
|
return HttpClientRequest.get("/containers/json").pipe(
|
||||||
|
HttpClientRequest.appendUrlParams({
|
||||||
|
all: params?.all ?? false,
|
||||||
|
limit: params?.limit,
|
||||||
|
size: params?.size ?? false,
|
||||||
|
filters: params?.filters,
|
||||||
|
}),
|
||||||
|
req => this.httpClient.execute(req),
|
||||||
|
Effect.andThen(res => res.json as Effect.Effect<readonly ContainerSummary[], ResponseError, never>)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const make: Effect.Effect<Docker153Client, never, HttpClient.HttpClient> = HttpClient.HttpClient.pipe(
|
export const make: Effect.Effect<Docker153Client, never, HttpClient.HttpClient> = HttpClient.HttpClient.pipe(
|
||||||
Effect.map(HttpClient.mapRequest(HttpClientRequest.prependUrl("/v1.53"))),
|
Effect.map(HttpClient.mapRequest(HttpClientRequest.appendUrl("/v1.53"))),
|
||||||
Effect.map(client => new Docker153ClientImpl(client)),
|
Effect.map(client => new Docker153ClientImpl(client)),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { HttpClient, type HttpClientError, HttpClientRequest } from "@effect/platform"
|
import { HttpClient, type HttpClientError } from "@effect/platform"
|
||||||
import { Effect, Match, Schema } from "effect"
|
import { Effect, Match, Schema } from "effect"
|
||||||
|
|
||||||
|
|
||||||
@@ -7,16 +7,12 @@ export type DockerBaseClientTypeId = typeof DockerBaseClientTypeId
|
|||||||
|
|
||||||
export interface DockerBaseClient {
|
export interface DockerBaseClient {
|
||||||
readonly [DockerBaseClientTypeId]: DockerBaseClientTypeId
|
readonly [DockerBaseClientTypeId]: DockerBaseClientTypeId
|
||||||
|
|
||||||
readonly httpClient: HttpClient.HttpClient.With<DockerClientError>
|
readonly httpClient: HttpClient.HttpClient.With<DockerClientError>
|
||||||
readonly httpContainerClient: HttpClient.HttpClient.With<DockerClientContainerError>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DockerBaseClientImpl implements DockerBaseClient {
|
export class DockerBaseClientImpl implements DockerBaseClient {
|
||||||
readonly [DockerBaseClientTypeId]: DockerBaseClientTypeId = DockerBaseClientTypeId
|
readonly [DockerBaseClientTypeId]: DockerBaseClientTypeId = DockerBaseClientTypeId
|
||||||
|
|
||||||
readonly httpClient: HttpClient.HttpClient.With<DockerClientError>
|
readonly httpClient: HttpClient.HttpClient.With<DockerClientError>
|
||||||
readonly httpContainerClient: HttpClient.HttpClient.With<DockerClientContainerError>
|
|
||||||
|
|
||||||
constructor(httpClient: HttpClient.HttpClient) {
|
constructor(httpClient: HttpClient.HttpClient) {
|
||||||
this.httpClient = httpClient.pipe(
|
this.httpClient = httpClient.pipe(
|
||||||
@@ -33,18 +29,6 @@ export class DockerBaseClientImpl implements DockerBaseClient {
|
|||||||
Match.orElse(Effect.fail),
|
Match.orElse(Effect.fail),
|
||||||
) as Effect.Effect<never, DockerClientError>),
|
) as Effect.Effect<never, DockerClientError>),
|
||||||
)
|
)
|
||||||
|
|
||||||
this.httpContainerClient = this.httpClient.pipe(
|
|
||||||
HttpClient.mapRequest(HttpClientRequest.appendUrl("/containers")),
|
|
||||||
HttpClient.filterStatus(status => status !== 404),
|
|
||||||
HttpClient.catchTag("ResponseError", e => Match.value(e.response.status).pipe(
|
|
||||||
Match.when(404, () => Effect.andThen(
|
|
||||||
e.response.json as Effect.Effect<HttpErrorResponse>,
|
|
||||||
({ message }) => new NoSuchContainer({ message }, true),
|
|
||||||
)),
|
|
||||||
Match.orElse(Effect.fail),
|
|
||||||
) as Effect.Effect<never, DockerClientContainerError>),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,5 +50,3 @@ export type DockerClientError = HttpClientError.HttpClientError | BadParameter |
|
|||||||
export class NoSuchContainer extends Schema.TaggedError<NoSuchContainer>("@effect-docker/DockerBaseClient/NoSuchContainer")("NoSuchContainer", {
|
export class NoSuchContainer extends Schema.TaggedError<NoSuchContainer>("@effect-docker/DockerBaseClient/NoSuchContainer")("NoSuchContainer", {
|
||||||
message: Schema.String,
|
message: Schema.String,
|
||||||
}) {}
|
}) {}
|
||||||
|
|
||||||
export type DockerClientContainerError = DockerClientError | NoSuchContainer
|
|
||||||
|
|||||||
@@ -1,40 +1,2 @@
|
|||||||
import net from "node:net"
|
export * as Docker153Client from "./Docker153Client.js"
|
||||||
import { HttpClient } from "@effect/platform"
|
export * as DockerBaseClient from "./DockerBaseClient.js"
|
||||||
import { NodeHttpClient, NodeRuntime } from "@effect/platform-node"
|
|
||||||
import { Effect, Layer } from "effect"
|
|
||||||
import { Agent } from "undici"
|
|
||||||
|
|
||||||
|
|
||||||
const DockerNodeSocketAgent = Effect.acquireUseRelease(
|
|
||||||
Effect.sync(() => {
|
|
||||||
console.log("acquire")
|
|
||||||
return net.createConnection({ path: "/var/run/docker.sock" })
|
|
||||||
}),
|
|
||||||
socket => Effect.sync(() => {
|
|
||||||
socket.on("connect", console.log)
|
|
||||||
socket.on("data", console.log)
|
|
||||||
return socket
|
|
||||||
}),
|
|
||||||
socket => Effect.sync(() => {
|
|
||||||
console.log("release")
|
|
||||||
socket.end()
|
|
||||||
}),
|
|
||||||
).pipe(
|
|
||||||
Effect.andThen(socket => NodeHttpClient.makeAgentLayer({ socket })),
|
|
||||||
Layer.unwrapScoped,
|
|
||||||
)
|
|
||||||
const DockerNodeHttpClient = Layer.provide(NodeHttpClient.layerWithoutAgent, DockerNodeSocketAgent)
|
|
||||||
|
|
||||||
const DockerUndiciDispatcher = Layer.succeed(NodeHttpClient.Dispatcher, new Agent({
|
|
||||||
socketPath: "/var/run/docker.sock"
|
|
||||||
}))
|
|
||||||
const DockerUndiciHttpClient = Layer.provide(NodeHttpClient.layerUndiciWithoutDispatcher, DockerUndiciDispatcher)
|
|
||||||
|
|
||||||
Effect.gen(function*() {
|
|
||||||
const client = yield* HttpClient.HttpClient
|
|
||||||
const response = yield* client.get("http://localhost/images/json")
|
|
||||||
console.log(yield* response.json)
|
|
||||||
}).pipe(
|
|
||||||
Effect.provide(DockerUndiciHttpClient),
|
|
||||||
NodeRuntime.runMain,
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
import { HttpClient } from "@effect/platform"
|
|
||||||
import { NodeHttpClient, NodeRuntime } from "@effect/platform-node"
|
|
||||||
import { Effect, Schema } from "effect"
|
|
||||||
|
|
||||||
|
|
||||||
export class NotFound extends Schema.TaggedError<NotFound>("NotFound")("NotFound", {
|
|
||||||
message: Schema.String,
|
|
||||||
}) {}
|
|
||||||
|
|
||||||
const program = Effect.gen(function*() {
|
|
||||||
const client = yield* HttpClient.HttpClient.pipe(
|
|
||||||
Effect.map(HttpClient.filterStatus(status => status !== 404)),
|
|
||||||
Effect.map(HttpClient.catchTag("ResponseError", e =>
|
|
||||||
e.response.status === 404
|
|
||||||
? Effect.andThen(e.response.text, message => Effect.fail(new NotFound({ message: "" })))
|
|
||||||
: Effect.fail(e)
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
const res = yield* client.get("https://docs.docker.com/adolf")
|
|
||||||
console.log(res.status)
|
|
||||||
// console.log(yield* res.text)
|
|
||||||
|
|
||||||
}).pipe(Effect.withSpan("program", {
|
|
||||||
attributes: { source: "Playground" }
|
|
||||||
}))
|
|
||||||
|
|
||||||
program.pipe(
|
|
||||||
Effect.provide(NodeHttpClient.layer),
|
|
||||||
NodeRuntime.runMain
|
|
||||||
)
|
|
||||||
@@ -34,5 +34,5 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
"include": ["./src", "./scripts"]
|
"include": ["./src"]
|
||||||
}
|
}
|
||||||
|
|||||||
58
packages/example/README.md
Normal file
58
packages/example/README.md
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# Effect FC
|
||||||
|
|
||||||
|
[Effect-TS](https://effect.website/) integration for React 19.2+ that allows you to write function components using Effect generators.
|
||||||
|
|
||||||
|
This library is in early development. While it is (almost) feature complete and mostly usable, expect bugs and quirks. Things are still being ironed out, so ideas and criticisms are more than welcome.
|
||||||
|
|
||||||
|
Documentation is currently being written. In the meantime, you can take a look at the `packages/example` directory.
|
||||||
|
|
||||||
|
## Peer dependencies
|
||||||
|
- `effect` 3.19+
|
||||||
|
- `react` & `@types/react` 19.2+
|
||||||
|
|
||||||
|
## Known issues
|
||||||
|
- React Refresh doesn't work for Effect FC's yet. Page reload is required to view changes. Regular React components are unaffected.
|
||||||
|
|
||||||
|
## What writing components looks like
|
||||||
|
```typescript
|
||||||
|
export class Todos extends Component.make("Todos")(function*() {
|
||||||
|
const state = yield* TodosState
|
||||||
|
const [todos] = yield* useSubscribables(state.ref)
|
||||||
|
|
||||||
|
yield* useOnMount(() => Effect.andThen(
|
||||||
|
Console.log("Todos mounted"),
|
||||||
|
Effect.addFinalizer(() => Console.log("Todos unmounted")),
|
||||||
|
))
|
||||||
|
|
||||||
|
const TodoFC = yield* Todo
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<Heading align="center">Todos</Heading>
|
||||||
|
|
||||||
|
<Flex direction="column" align="stretch" gap="2" mt="2">
|
||||||
|
<TodoFC _tag="new" />
|
||||||
|
|
||||||
|
{Chunk.map(todos, todo =>
|
||||||
|
<TodoFC key={todo.id} _tag="edit" id={todo.id} />
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}) {}
|
||||||
|
|
||||||
|
const TodosStateLive = TodosState.Default("todos")
|
||||||
|
|
||||||
|
const Index = Component.make("Index")(function*() {
|
||||||
|
const context = yield* useContext(TodosStateLive)
|
||||||
|
const TodosFC = yield* Effect.provide(Todos, context)
|
||||||
|
|
||||||
|
return <TodosFC />
|
||||||
|
}).pipe(
|
||||||
|
Component.withRuntime(runtime.context)
|
||||||
|
)
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/")({
|
||||||
|
component: Index
|
||||||
|
})
|
||||||
|
```
|
||||||
8
packages/example/biome.json
Normal file
8
packages/example/biome.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://biomejs.dev/schemas/latest/schema.json",
|
||||||
|
"root": false,
|
||||||
|
"extends": "//",
|
||||||
|
"files": {
|
||||||
|
"includes": ["./src/**"]
|
||||||
|
}
|
||||||
|
}
|
||||||
23
packages/example/package.json
Normal file
23
packages/example/package.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "@effect-docker/example",
|
||||||
|
"type": "module",
|
||||||
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"lint:tsc": "tsc --noEmit",
|
||||||
|
"lint:biome": "biome lint",
|
||||||
|
"clean:cache": "rm -rf .turbo",
|
||||||
|
"clean:dist": "rm -rf dist",
|
||||||
|
"clean:modules": "rm -rf node_modules"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"tsx": "^4.21.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@effect/platform": "^0.94.4",
|
||||||
|
"@effect/platform-bun": "^0.87.1",
|
||||||
|
"@effect/platform-node": "^0.104.1",
|
||||||
|
"effect": "^3.19.16",
|
||||||
|
"effect-docker": "workspace:*",
|
||||||
|
"undici": "^7.19.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
35
packages/example/src/socket.ts
Normal file
35
packages/example/src/socket.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { HttpClient, HttpClientRequest } from "@effect/platform"
|
||||||
|
import { NodeHttpClient, NodeRuntime } from "@effect/platform-node"
|
||||||
|
import { Effect, Layer } from "effect"
|
||||||
|
import { Docker153Client } from "effect-docker"
|
||||||
|
import { Agent } from "undici"
|
||||||
|
|
||||||
|
|
||||||
|
const DockerSocketHttpClient = Layer.effect(
|
||||||
|
HttpClient.HttpClient,
|
||||||
|
Effect.map(HttpClient.HttpClient, HttpClient.mapRequest(HttpClientRequest.prependUrl("http://localhost"))),
|
||||||
|
).pipe(
|
||||||
|
Layer.provide(NodeHttpClient.layerUndiciWithoutDispatcher),
|
||||||
|
Layer.provide(
|
||||||
|
Layer.succeed(NodeHttpClient.Dispatcher, new Agent({
|
||||||
|
socketPath: "/var/run/docker.sock"
|
||||||
|
}))
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
export class DockerClient extends Effect.Service<DockerClient>()("@effect-docker/example/socket/DockerClient", {
|
||||||
|
effect: Docker153Client.make,
|
||||||
|
dependencies: [DockerSocketHttpClient],
|
||||||
|
}) {}
|
||||||
|
|
||||||
|
const withService = Effect.gen(function*() {
|
||||||
|
const docker = yield* DockerClient
|
||||||
|
const containers = yield* docker.listContainers({ all: true })
|
||||||
|
console.log(containers)
|
||||||
|
})
|
||||||
|
|
||||||
|
withService.pipe(
|
||||||
|
Effect.provide(DockerClient.Default),
|
||||||
|
NodeRuntime.runMain,
|
||||||
|
)
|
||||||
38
packages/example/tsconfig.json
Normal file
38
packages/example/tsconfig.json
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
// Enable latest features
|
||||||
|
"lib": ["ESNext", "DOM"],
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "NodeNext",
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
// "allowJs": true,
|
||||||
|
|
||||||
|
// Bundler mode
|
||||||
|
"moduleResolution": "NodeNext",
|
||||||
|
// "allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
// "noEmit": true,
|
||||||
|
|
||||||
|
// Best practices
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
|
||||||
|
// Some stricter flags (disabled by default)
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"noPropertyAccessFromIndexSignature": false,
|
||||||
|
|
||||||
|
// Build
|
||||||
|
"outDir": "./dist",
|
||||||
|
"declaration": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
|
||||||
|
"plugins": [
|
||||||
|
{ "name": "@effect/language-service" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
"include": ["./src"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user