87114ab186
apple / swift (push) Successful in 1m15s
apple / screenshots (push) Successful in 4m21s
ci / web (push) Successful in 52s
ci / docs-site (push) Successful in 1m0s
android / android (push) Has been cancelled
arch / build-publish (push) Has been cancelled
ci / rust (push) Has been cancelled
ci / bench (push) Has been cancelled
deb / build-publish (push) Has been cancelled
decky / build-publish (push) Has been cancelled
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 18s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 15s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 14s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 16s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 13s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 14m56s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 19m42s
docker / deploy-docs (push) Successful in 26s
New top-level sdk/ package (RFC §7): a typed management-API client plus the lifecycle event stream, built on Effect, two surfaces over one core: - @punktfunk/host — the Promise facade front door: connect() resolves URL/token/TLS pin from the host's own files (zero config on the box), fails fast on bad credentials, pf.events.on() with typed callbacks (exact kinds, domain.* prefixes, "*", "dropped", "unknown"), pf.request() for the REST surface. Effect never required. - @punktfunk/host/effect — the PunktfunkHost service + PunktfunkHostLive layer, Stream-based events()/eventsRaw(), typed errors (AuthError | ApiError | TransportError | VersionSkew — a 2xx that fails its schema is a typed skew, not undefined later), and every wire shape as an effect/Schema: REST generated via orval client:'effect' from api/openapi.json (S3 spike: works well; the text/event-stream payload is out of its reach), events hand-mirrored from the host's snapshot-tested wire format as a kind-discriminated union. One reconnecting SSE core under both surfaces: spec-shaped parser, exponential+jittered backoff (capped, resets after a healthy connection), Last-Event-ID resume, 401 terminal. Default is LIVE tail only — a fresh notify script must not re-fire on the host's replayed ring (since: 0 opts into full replay). TLS: the pin trusts exactly the host's self-signed identity cert (chain-verified; hostname check waived — the cert is deliberately CN-only for fingerprint pinning). Bun via fetch tls, Node via an undici dispatcher (optionalDependency). definePlugin() accepts both main shapes (async fn | Effect requiring PunktfunkHost). Examples in both styles; README carries the compat contract + systemd/Task Scheduler templates. 11 bun tests green (wire decode against the Rust snapshot strings, SSE parser/reconnect/Last-Event-ID/401, both surfaces vs a mock host). Live-verified against a real host on Bun AND Node through the pinned loopback hop: connect → REST mutate → live event received → resume cursor advanced; a wrong CA is rejected. npm publish + CI wiring deferred (npm org = RFC open question 1). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
71 lines
2.5 KiB
TypeScript
71 lines
2.5 KiB
TypeScript
// `@punktfunk/host/effect` — the Effect-native surface (RFC §7): the `PunktfunkHost` service
|
|
// tag + live layer, the wire schemas, typed errors, and Stream accessors. The package root
|
|
// (`@punktfunk/host`) is the Promise facade; this entry is for plugins and Effect programs.
|
|
//
|
|
// import { Effect, Stream } from "effect";
|
|
// import { PunktfunkHost, PunktfunkHostLive, events } from "@punktfunk/host/effect";
|
|
//
|
|
// const program = Effect.gen(function* () {
|
|
// yield* events().pipe(
|
|
// Stream.filter((e) => e.kind === "stream.started"),
|
|
// Stream.runForEach((e) => Effect.log(`stream started: ${e.stream.mode}`)),
|
|
// );
|
|
// });
|
|
// Effect.runPromise(program.pipe(Effect.provide(PunktfunkHostLive())));
|
|
import { Effect, type Schema as S, Stream } from "effect";
|
|
import {
|
|
EventStreamError,
|
|
PunktfunkHost,
|
|
type RequestError,
|
|
type VersionSkew,
|
|
} from "./client.js";
|
|
import type { EventStreamOptions, SseFrame } from "./sse.js";
|
|
import type { HostEvent } from "./wire.js";
|
|
|
|
export {
|
|
ApiError,
|
|
AuthError,
|
|
EventStreamError,
|
|
layer,
|
|
makeService,
|
|
PunktfunkHost,
|
|
type PunktfunkHostService,
|
|
PunktfunkHostLive,
|
|
type RequestError,
|
|
SseAuthError,
|
|
TransportError,
|
|
VersionSkew,
|
|
} from "./client.js";
|
|
export { type ConnectOptions, configDir, resolveConfig } from "./config.js";
|
|
export type { EventStreamOptions, SseFrame } from "./sse.js";
|
|
export * from "./wire.js";
|
|
/** The generated REST wire schemas (orval `client: 'effect'` over `api/openapi.json`). */
|
|
export * as api from "./gen/schemas.js";
|
|
|
|
/** The decoded lifecycle-event stream of the ambient [`PunktfunkHost`]. */
|
|
export const events = (
|
|
opts?: EventStreamOptions,
|
|
): Stream.Stream<HostEvent, EventStreamError, PunktfunkHost> =>
|
|
Stream.unwrap(Effect.map(PunktfunkHost, (s) => s.events(opts)));
|
|
|
|
/** Every SSE frame verbatim (the `dropped` marker + unknown kinds included). */
|
|
export const eventsRaw = (
|
|
opts?: EventStreamOptions,
|
|
): Stream.Stream<SseFrame, EventStreamError, PunktfunkHost> =>
|
|
Stream.unwrap(Effect.map(PunktfunkHost, (s) => s.eventsRaw(opts)));
|
|
|
|
/** One management-API request under `/api/v1` on the ambient service. */
|
|
export const request = (
|
|
method: string,
|
|
path: string,
|
|
body?: unknown,
|
|
): Effect.Effect<unknown, RequestError, PunktfunkHost> =>
|
|
Effect.flatMap(PunktfunkHost, (s) => s.request(method, path, body));
|
|
|
|
/** GET + schema-validate on the ambient service (schemas from [`api`]). */
|
|
export const get = <A, I>(
|
|
path: string,
|
|
schema: S.Schema<A, I>,
|
|
): Effect.Effect<A, RequestError | VersionSkew, PunktfunkHost> =>
|
|
Effect.flatMap(PunktfunkHost, (s) => s.get(path, schema));
|