Skip to content

feat(process): add processApi with middleware support#159

Open
taras wants to merge 6 commits intomainfrom
feat/process-api
Open

feat(process): add processApi with middleware support#159
taras wants to merge 6 commits intomainfrom
feat/process-api

Conversation

@taras
Copy link
Copy Markdown
Member

@taras taras commented Feb 16, 2026

Motivation

Add middleware support to @effectionx/process using Effection's new createApi feature, enabling logging, mocking, and instrumentation of process execution for testing and observability.

This is part of a series of PRs converting effectionx packages to use the new createApi pattern:

Approach

  • Created processApi with a createProcess operation that middleware can intercept
  • Updated exec() to use the API internally while maintaining backward compatibility
  • Added middleware tests to verify scoped behavior
  • Updated README with middleware documentation and v4.1 disclaimer
  • Uses pkg.pr.new/thefrontside/effection@1116 preview for createApi support

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 16, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a middleware-capable public API processApi (with around() hooks) that all process creation now routes through; re-exports processApi, removes the old daemon module file, updates README and tests to document and verify middleware scoping, and bumps the process package version and effection peer constraint. (49 words)

Changes

Cohort / File(s) Summary
Repository config
package.json, process/package.json
Pinned devDependency resolution for effection to a preview registry URL and added pnpm overrides; bumped process package version to 0.8.0, added files entries, and tightened peer dependency to ^4.1.0-alpha.3.
Documentation
process/README.md
Added Effection v4.1+ prerequisite and extensive docs/examples for the new public processApi and its around() middleware semantics and scoping.
Module re-exports
process/mod.ts
Removed re-export of the old daemon symbols and added an explicit re-export of processApi from ./src/exec.ts.
Core implementation
process/src/exec.ts
Introduced processApi: Api<ProcessApiCore> exposing exec and daemon; routed exec/daemon flows through the API (via doExec), added public Daemon type and daemon() helper, and mapped daemon exit to DaemonExitError.
Daemon removal
process/src/daemon.ts
Deleted the previous daemon module and its exported Daemon interface and daemon() implementation.
Tests
process/test/exec.test.ts, effect-ts/effect.test.ts, stream-helpers/valve.test.ts
Updated tests to use processApi; added middleware-scoping tests for processApi.around; skipped/annotated several tests due to Effection preview compatibility and adjusted imports to remove duplicates.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Exec
    participant DoExec
    participant API
    participant Middleware
    participant OS

    Client->>Exec: exec(cmd, opts)
    Exec->>DoExec: route via doExec
    DoExec->>API: operations.exec(cmd, opts)
    API->>Middleware: around(exec) invoked
    Middleware->>Middleware: inspect/modify args
    Middleware->>API: call next(...)
    API->>OS: create/run process
    OS-->>API: Process
    API-->>DoExec: Process
    DoExec-->>Exec: Process
    Exec-->>Client: Process
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Suggested reviewers

  • cowboyd
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately and concisely summarizes the main change: adding processApi with middleware support to the process package, which aligns with the primary objective and modifications shown across all files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main
Policy Compliance ✅ Passed All applicable policies are compliant. The process/package.json has a properly formatted description meeting requirements, and the root package.json is private and exempt. Version bump from 0.7.2 to 0.8.0 appropriately reflects new features added.
Description check ✅ Passed The PR description clearly addresses both motivation and approach sections with specific details about middleware support, API implementation, and version requirements.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/process-api

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Feb 16, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@effectionx/process@159

commit: 5e976a8

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@process/package.json`:
- Around line 14-16: Update the package.json version field to a minor bump to
reflect the new public API surface (e.g., change "version": "0.7.2" to "0.8.0");
ensure the package.json "version" value is updated consistently and include an
entry in the release notes/changelog if present so the new middleware-capable
API is recorded before publishing.

In `@process/README.md`:
- Around line 230-233: The heading "#### Capturing process executions for
testing" is jumping levels; change this heading to one level higher (use "###
Capturing process executions for testing") so the Markdown increments by one
level; update the heading text in the README.md where "Capturing process
executions for testing" appears to fix MD001 and keep surrounding headings
consistent with the rest of the document.

In `@process/test/exec.test.ts`:
- Around line 526-569: The tests use the shell builtin "echo" which fails on
Windows; update the processApi.around tests to invoke a real executable instead
of a shell builtin by calling exec with a binary and args (e.g., use "node" with
"-e" and a small console.log payload) so the created command tokens and
middleware behavior are preserved; modify both uses of exec("echo ...") in the
tests around the createProcess middleware (refer to processApi.around,
createProcess, exec, and spawn) to call exec with the executable and argument
array form to ensure cross-platform compatibility.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@process/package.json`:
- Around line 14-15: The peerDependencies entry currently pins effection to a
pre-release ("^4.1.0-alpha.3") which is overly restrictive; update the
package.json peerDependencies key for "effection" to the project-standard range
"^3 || ^4" so consumers of both Effection 3.x and 4.x stable releases are
supported; modify the "peerDependencies" block (the "effection" entry)
accordingly and run a quick install/test to verify no semver conflicts.

In `@process/README.md`:
- Around line 6-9: Update the version note text that currently reads "Note:
Starting with version 0.2.0, this package requires Effection v4.1 or greater..."
to reflect the actual release that introduced the Effection v4.1 requirement:
locate the markdown paragraph containing that exact sentence (the "Note:
Starting with version 0.2.0..." line) in README and change "0.2.0" to the
correct version number for this release so the note accurately states which
version requires Effection v4.1.
- Around line 236-249: The example's logged output is incorrect because the
middleware in processApi.around's createProcess receives parsed command and args
(args[0] is the command name like "git" or "npm"), so change the example to
either join the command and its arguments before pushing (e.g. push `${args[0]}
${args.slice(1).join(' ')}`) or update the expected console output to reflect
only the command names (e.g. ["git","npm"]); update the example using the
createProcess middleware and exec invocation to show consistent input and
output.

* Create a process operation.
* This is the core operation that middleware can intercept.
*/
createProcess(cmd: string, opts: ExecOptions): Operation<Process>;
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the APi should be exec and daemon, not createProcess

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
process/src/exec.ts (1)

136-202: 🧹 Nitpick | 🔵 Trivial

Consider potential double-execution edge case.

Each method ([Symbol.iterator], join, expect) independently calls doExec(), which creates a new process. While the typical usage patterns (exec().join() or let p = yield* exec()) are safe, the following would unexpectedly spawn two processes:

let e = exec("cmd");
yield* e;           // process 1
yield* e.join();    // process 2 (Exec.join, not Process.join)

This appears to be the existing design, not a regression. Consider adding a JSDoc note to clarify the intended usage patterns.

🤖 Fix all issues with AI agents
In `@process/mod.ts`:
- Around line 1-3: Remove the redundant explicit re-export of processApi: the
existing export * from "./src/exec.ts" already re-exports processApi, so delete
the line exporting processApi and, if you want to keep discoverability, add a
short doc comment above the barrel export (export * from "./src/exec.ts")
mentioning that processApi is available from this module; reference symbols:
processApi and the export * from "./src/exec.ts".

In `@process/src/exec.ts`:
- Line 6: The import of createApi in process/src/exec.ts is invalid for the
declared Effection version and will break at runtime; remove createApi from the
effection import list and refactor any code in exec.ts that calls createApi to
use a supported Effection primitive (e.g., replace with an equivalent using
available APIs such as createResource/spawn/Operation or a small local wrapper),
or stub the functionality with a clear TODO comment; once
thefrontside/effection#1116 lands and you bump the effection dependency, restore
createApi and remove the temporary workaround, and add/adjust tests to cover the
altered code path to prevent regressions.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@process/README.md`:
- Line 231: Change the heading "Intercepting daemon calls" (and the similar
heading at line 251) to use level ### instead of #### so the heading level does
not jump after the `processApi` section; locate the occurrences of the text
"Intercepting daemon calls" in README.md and replace the leading four hashes
with three to restore proper heading hierarchy under `processApi`.

In `@process/src/exec.ts`:
- Around line 3-9: Change the import for the type-only symbol Api to a
TypeScript type-only import to improve tree-shaking and clarity: update the
import statement that currently brings in Api, Operation, createApi, resource,
spawn so that Api is imported with the type modifier (keep Operation as-is if
it’s used at runtime). Locate the import at the top of the file (references:
Api, Operation, createApi, resource, spawn) and modify only the Api import to be
type-only.
- Around line 137-146: doExec() is being invoked multiple times (in the
iterator, join(), and expect()), which spawns duplicate processes; change the
implementation to lazily create and cache the single process/operation result
returned by processApi.operations.exec(command, options) (e.g., store it in a
local variable like cachedOp or createdProcess) and have *[Symbol.iterator](),
*join(), and *expect() all yield from that cached value so the process is
created once and reused; if maintaining mutable cached state across yields is
concerning, use Effection's resource/useScope pattern to manage the single
process lifecycle instead.

---

Duplicate comments:
In `@process/mod.ts`:
- Around line 2-3: The explicit re-export of processApi is redundant because the
existing barrel export (export *) already exposes processApi; remove the
explicit line "export { processApi } from \"./src/exec.ts\";" or instead convert
the preceding comment into a JSDoc on the barrel export to preserve
discoverability, leaving only the single barrel export that exposes processApi
(symbol: processApi) and avoiding duplicate exports.

In `@process/package.json`:
- Around line 15-16: The peerDependencies entry currently pins effection to the
pre-release "^4.1.0-alpha.3" for the createApi feature; update the project docs
to clearly state this intentional constraint (add a note in the package README
or CHANGELOG referencing the peerDependencies key and the createApi feature and
why effection 4.1-alpha is required), and add a TODO comment in package.json
maintenance notes to change the version to "^4.1" (or broaden back to "^3 || ^4"
if compatible) once Effection 4.1.0 is stable; keep the existing "effection":
"^4.1.0-alpha.3" entry until that follow-up is done.

In `@process/src/exec.ts`:
- Line 6: The import of createApi in process/src/exec.ts depends on an unmerged
Effection PR (`#1116`); remove the direct import of createApi (and any usage of it
in this module) and replace it with a safe fallback that either (a) uses an
alternative local implementation or helper, or (b) throws a clear runtime error
explaining the upstream dependency until `#1116` is merged; update any functions
that call createApi to use the fallback or guard them behind a feature flag so
the package can be published safely without the upstream PR.

@@ -1,5 +1,5 @@
import { type Effect, type Exit, Layer, ManagedRuntime } from "effect";
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebase on main this PR so this will disappear

Comment on lines +6 to +9
> **Note**: Starting with version 0.8.0, this package requires Effection v4.1 or greater
> for full functionality. The middleware/API features (`processApi`) require the new
> `createApi` function introduced in Effection v4.1.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is no longer true, we should remove

expect(): Operation<ExitStatus>;
}

### `processApi`
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to be updated to mach casing

@taras taras force-pushed the feat/process-api branch from 3908334 to 24b447d Compare March 16, 2026 14:09
process/api.ts Outdated
Comment on lines +33 to +38
/**
* Core interface for the process API operations.
* Used internally by createApi to enable middleware support.
*/
export interface ProcessApiCore {
/**
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See FS PR, but Core in this context is a throw-away word

process/api.ts Outdated
Comment on lines +133 to +143
export function exec(command: string, options: ExecOptions = {}): Exec {
function* doExec(): Operation<Process> {
return yield* coreExec(command, options);
}

return {
*[Symbol.iterator]() {
return yield* doExec();
},
*join() {
const process = yield* doExec();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with code rabbit here. Highly suspicious

Introduce @effectionx/context-api integration for @effectionx/process,
allowing middleware interception of process creation. Rename from
createProcess to exec/daemon per review feedback, use ProcessHandler
naming convention, bump version to 0.8.0.
@taras taras force-pushed the feat/process-api branch from 24b447d to 5ca52ba Compare March 27, 2026 03:28
process/api.ts Outdated
},

*daemon(command: string, options: ExecOptions): Operation<Daemon> {
let process = yield* ProcessApi.operations.exec(command, options);
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this not inside the resource?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants