PHPackages                             medz/stream-wrapper-interface - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. medz/stream-wrapper-interface

AbandonedLibrary[Utility &amp; Helpers](/categories/utility)

medz/stream-wrapper-interface
=============================

Common interface for streamWrapper class.

v1.0.1(9y ago)226511MITTypeScriptPHP &gt;=5.3

Since Sep 1Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/medz/stream-wrapper-interface)[ Packagist](https://packagist.org/packages/medz/stream-wrapper-interface)[ Docs](http://medz.cn)[ RSS](/packages/medz-stream-wrapper-interface/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (4)DependenciesVersions (4)Used By (1)

cf-gait-workflow
================

[](#cf-gait-workflow)

Semantic tracing helpers for Cloudflare Workflows.

`cf-gait-workflow` keeps Cloudflare Workflows as the source of execution truth while adding typed lifecycle events around workflow steps, sleeps, and event waits. Use it when you want workflow history to stay native, but also need structured telemetry for logs, traces, metrics, or custom observers.

Install
-------

[](#install)

```
bun add cf-gait-workflow
```

```
npm install cf-gait-workflow
```

Quick Start
-----------

[](#quick-start)

```
import {
  defineGaitEmitter,
  defineGaitWorkflowEntrypoint,
} from "cf-gait-workflow";

export const GaitEmitter = defineGaitEmitter((event, ctx) => {
  console.log(event, ctx);
});

export const Workflow = defineGaitWorkflowEntrypoint(async (event, gait) => {
  const result = await gait.step("fetch data", async (ctx) => {
    return {
      attempt: ctx.attempt,
      input: event.payload,
    };
  });

  await gait.sleep("cooldown", { duration: "1 minute" });

  const approval = await gait.event("approval", {
    type: "approval",
    timeout: "1 hour",
  });

  return {
    result,
    approval: approval.payload,
  };
});
```

`defineGaitWorkflowEntrypoint` creates a normal Cloudflare `WorkflowEntrypoint`. The only difference is that your `run` plan receives a `gait` helper next to the original workflow event.

Emitter
-------

[](#emitter)

Gait events are delivered to an exported Worker entrypoint. By default the workflow looks for an export named `GaitEmitter`.

```
import { defineGaitEmitter } from "cf-gait-workflow";

export const GaitEmitter = defineGaitEmitter((event, ctx) => {
  console.log(JSON.stringify({ event, ctx }));
});
```

The callback is typed as the full gait event union. Every emitted context has a numeric `timestamp` field. When events are emitted by `gait.step`, `gait.sleep`, or `gait.event`, the timestamp is added automatically with `Date.now()`.

If you want a different export name, pass it to `defineGaitWorkflowEntrypoint`:

```
export const Events = defineGaitEmitter((event, ctx) => {
  console.log(event, ctx);
});

export const Workflow = defineGaitWorkflowEntrypoint(
  "Events",
  async (_event, gait) => {
    return await gait.step("work", async () => "ok");
  },
);
```

Gait Helper
-----------

[](#gait-helper)

The helper currently exposes:

- `gait.step(name, callback, rollbackOptions?)`
- `gait.step(name, config, callback, rollbackOptions?)`
- `gait.sleep(name, params)`
- `gait.event(name, options)`

### `gait.step`

[](#gaitstep)

`gait.step` mirrors Cloudflare `step.do`. It preserves the original step name, config, retry behavior, callback context, and rollback options.

```
await gait.step("plain step", async (ctx) => {
  return {
    name: ctx.step.name,
    count: ctx.step.count,
    attempt: ctx.attempt,
  };
});

await gait.step(
  "configured step",
  { retries: { limit: 3, delay: 1_000 } },
  async () => {
    return "done";
  },
);
```

Events:

- `step:start`: emitted before your callback runs.
- `step:complete`: emitted with `output` after the callback resolves.
- `step:error`: emitted with `error` before the original error is rethrown.

Step failures are not wrapped. The original error is rethrown so native Workflow retry behavior is preserved.

### `gait.sleep`

[](#gaitsleep)

`gait.sleep` delegates to Cloudflare `step.sleep` or `step.sleepUntil`, depending on the input.

```
await gait.sleep("seconds", 30);
await gait.sleep("duration", { duration: "5 minutes" });
await gait.sleep("date", new Date(Date.now() + 60_000));
await gait.sleep("timestamp", { timestamp: Date.now() + 60_000 });
```

Events:

- `sleep:start`: emitted with the original `params`.
- `sleep:complete`: emitted when the sleep resolves.
- `sleep:error`: emitted with `error` if the sleep fails.

Sleep failures are wrapped in `NonRetryableWithRawError`. The wrapper exposes the original error through `.raw`.

### `gait.event`

[](#gaitevent)

`gait.event` waits for a Cloudflare Workflow event and emits lifecycle telemetry around that wait.

```
const approval = await gait.event("approval", {
  type: "approval",
  timeout: "1 hour",
});

console.log(approval.payload);
```

The actual wait still uses `step.waitForEvent(name, options)`. Gait also wraps the wait in a durable step named `gait:event/` so `event:start`, `event:complete`, and `event:error` stay inside a Workflow step boundary during replay or restart.

The emitted `step` object keeps the logical event name:

```
{
  step: { name: "approval", count: 1 }
}
```

The count is scoped by logical event name because the wrapper step name is also derived from that name. For example, `approval`, then `review`, then `approval`emits counts `1`, `1`, and `2`.

Event wait failures are wrapped in `NonRetryableWithRawError`. The wrapper exposes the original error through `.raw`.

Event Types
-----------

[](#event-types)

`defineGaitEmitter` infers the event name and context union for its callback, so most applications do not need manual annotations.

Current event names:

```
type EventName =
  | "step:start"
  | "step:complete"
  | "step:error"
  | "sleep:start"
  | "sleep:complete"
  | "sleep:error"
  | "event:start"
  | "event:complete"
  | "event:error";
```

All contexts include:

```
{
  step: { name: string; count: number };
  timestamp: number;
}
```

`step:*` events include the native Cloudflare `WorkflowStepContext` fields. `sleep:start` includes `params`. `event:start` includes `options`. `*:complete` events include `output` where an output exists. `*:error` events include `error`.

Manual Integration
------------------

[](#manual-integration)

If you already have a `WorkflowEntrypoint` class, use `createGaitWorkflow`inside `run`:

```
import { WorkflowEntrypoint } from "cloudflare:workers";
import { createGaitWorkflow } from "cf-gait-workflow";

export class Workflow extends WorkflowEntrypoint {
  override async run(event, step) {
    const gait = createGaitWorkflow({ event, step });

    return await gait.step("work", async () => {
      return "ok";
    });
  }
}
```

Pass `binding` when the emitter export is not `GaitEmitter`:

```
const gait = createGaitWorkflow({
  event,
  step,
  binding: "Events",
});
```

If the emitter export cannot be found, gait throws a Cloudflare `NonRetryableError`.

Cloudflare Setup
----------------

[](#cloudflare-setup)

Export the emitter and workflow from your Worker:

```
export const GaitEmitter = defineGaitEmitter((event, ctx) => {
  console.log(event, ctx);
});

export const Workflow = defineGaitWorkflowEntrypoint(async (_event, gait) => {
  await gait.step("work", async () => "ok");
});
```

Then bind the workflow in Wrangler as you would for any Cloudflare Workflow. See [`playground/worker.ts`](./playground/worker.ts) and [`playground/wrangler.jsonc`](./playground/wrangler.jsonc) for a runnable example.

Development
-----------

[](#development)

```
bun install
bun run test
bun run build
```

Run the playground Worker:

```
cd playground
bun run dev
```

License
-------

[](#license)

MIT. See [LICENSE](./LICENSE).

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance58

Moderate activity, may be stable

Popularity18

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity60

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~2 days

Total

2

Last Release

3632d ago

PHP version history (2 changes)v1.0.0PHP &gt;=5.4

v1.0.1PHP &gt;=5.3

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/5564821?v=4)[Seven Du](/maintainers/medz)[@medz](https://github.com/medz)

---

Top Contributors

[![medz](https://avatars.githubusercontent.com/u/5564821?v=4)](https://github.com/medz "medz (7 commits)")

---

Tags

cloudflarecloudflare-workerscloudflare-workflowsworkflowphpinterfacewrapperstreamwrappermedzwrapperInterface

### Embed Badge

![Health badge](/badges/medz-stream-wrapper-interface/health.svg)

```
[![Health](https://phpackages.com/badges/medz-stream-wrapper-interface/health.svg)](https://phpackages.com/packages/medz-stream-wrapper-interface)
```

###  Alternatives

[samrap/acf-fluent

A fluent interface for the Advanced Custom Fields WordPress plugin

28358.2k4](/packages/samrap-acf-fluent)[samrap/gestalt

Gestalt is a simple, elegant PHP package for managing your framework's configuration values.

163.7k3](/packages/samrap-gestalt)[wujunze/money-wrapper

MoneyPHP Wrapper

103.8k](/packages/wujunze-money-wrapper)[mishahawthorn/ocrmypdf

A simple PHP wrapper for OCRmyPDF

101.9k](/packages/mishahawthorn-ocrmypdf)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
