Skip to content

build: migrate effect v3 → v4 (beta.103) - #747

Open
ryanbas21 wants to merge 3 commits into
mainfrom
chore/effect-v4-migration
Open

build: migrate effect v3 → v4 (beta.103)#747
ryanbas21 wants to merge 3 commits into
mainfrom
chore/effect-v4-migration

Conversation

@ryanbas21

@ryanbas21 ryanbas21 commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Summary

Migrates the entire SDK from effect@^3.20 (v3) to effect@4.0.0-beta.103 (v4 beta).

Changes by package

pnpm-workspace.yaml (catalog)

  • effect: '4.0.0-beta.103'
  • @effect/vitest: '4.0.0-beta.103'
  • @effect/platform-node: '4.0.0-beta.103'
  • Removed @effect/platform and @effect/cli (consolidated into effect core)
  • Bumped vitest catalog to ^4.1.0

packages/sdk-utilities

  • micro.utils.ts: MicroExit/exitIsFail/exitIsSuccess/causeIsDieExit/Cause; renamed handleMicroExithandleExit
  • config.types/utils/effects/test: EitherResult (succeed/fail/isSuccess/isFailure)

packages/journey-client

  • journey.utils.ts: Either.right/leftResult.succeed/fail
  • client.store.ts: Either.matchResult.match (onFailure/onSuccess)
  • Tests: _tag: 'Right'/'Left''Success'/'Failure'; .right/.left.success/.failure

packages/oidc-client (12 files)

  • All Micro.*Effect.*
  • Cause.failureOptionCause.findErrorOption (returns Option<E>)
  • exitIsFail(exit)Cause.findErrorOption(exit.cause) + Option.isSome
  • Tests: added vi.clearAllMocks() in afterEach to fix accumulated mock call counts

packages/davinci-client (5 files)

  • All Micro.*Effect.*
  • Effect.repeat({while})Effect.gen + manual loop (v4 while receives schedule output type, not effect output)
  • password-policy.rules.ts: Array.filterMap now expects Result callbacks; Option.some/none()Result.succeed/failVoid
  • Context.TagContext.Service

e2e/mock-api-v2 (full migration)

  • All from '@effect/platform'from 'effect/unstable/httpapi'
  • Context.TagContext.Service
  • Effect.ifEffect.suspend(() => bool ? ... : ...)
  • Option.fromNullableOption.fromNullishOr
  • Schema.Union(a, b)Schema.Union([a, b])
  • Schema.Record({key, value})Schema.Record(key, value)
  • Schema.Schema<T, T>Schema.Schema<T>
  • HttpApiMiddleware.TagHttpApiMiddleware.Service<Self, {provides: T}>()
  • Fixed build script: nxBuildbuild

Verification

Check Result
TypeScript build (all packages) ✅ clean
Unit tests ✅ 887/887 (79 test files)
Lint ✅ 0 errors
Prettier ✅ clean
E2E journey-suites (uses mock-api-v2) ✅ 15/15 passed (2 skipped, pre-existing)

Summary by CodeRabbit

  • Bug Fixes

    • Improved polling behavior with more reliable delays, timeout handling, and error reporting.
    • Strengthened authorization, session, token, and user-information error handling.
    • Improved mock API request processing, cookie handling, validation, and response behavior.
  • Improvements

    • Updated public client response types to better represent loading, success, and failure states.
    • Improved configuration and journey response parsing while preserving existing behavior.
  • Tests

    • Expanded and updated coverage for authorization, session, logout, token exchange, polling, and configuration scenarios.

Migrate all packages from effect@^3.20 to effect@4.0.0-beta.103.

Key changes:
- Replace Micro with Effect throughout oidc-client and davinci-client
- Migrate Either to Result in sdk-utilities and journey-client
- Migrate MicroExit/exitIsFail/exitIsSuccess to Exit/Cause API
- Update e2e/mock-api-v2 from @effect/platform to effect/unstable/httpapi
- Bump vitest catalog to ^4.1.0 to match @effect/vitest peer requirement
- Remove consolidated packages: @effect/platform, @effect/cli
- Fix handleMicroExit → handleExit (renamed)
- Add vi.clearAllMocks() to oidc-client afterEach (accumulated mock counts)
@changeset-bot

changeset-bot Bot commented Aug 5, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 02cb3c2

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@ryanbas21, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 30 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4a7664f4-2dee-459b-ab5b-38a94589c77a

📥 Commits

Reviewing files that changed from the base of the PR and between 0f15944 and 02cb3c2.

📒 Files selected for processing (2)
  • packages/davinci-client/src/lib/client.store.effects.ts
  • packages/davinci-client/src/lib/password-policy.rules.ts
📝 Walkthrough

Walkthrough

The PR migrates the repository to Effect 4 APIs. It updates the mock HTTP API, client effects, Result handling, service declarations, HTTP clients, schemas, API reports, and package catalogs.

Changes

Mock HTTP API migration

Layer / File(s) Summary
HTTP contracts and schemas
e2e/mock-api-v2/src/spec.ts, e2e/mock-api-v2/src/schemas/...
Endpoints use object-based configuration. Schema unions and type declarations use updated Effect APIs.
Middleware and service wiring
e2e/mock-api-v2/src/middleware/..., e2e/mock-api-v2/src/services/...
Middleware and services use Context.Service and execute downstream HTTP effects.
Handler and server composition
e2e/mock-api-v2/src/handlers/..., e2e/mock-api-v2/src/main.ts
Handlers use unstable HTTP modules. Server assembly uses HttpRouter and explicit application layers.

Effect runtime migration

Layer / File(s) Summary
DaVinci polling and FIDO effects
packages/davinci-client/src/lib/client.store.effects.ts, packages/davinci-client/src/lib/fido/fido.ts
Polling and FIDO flows use Effect, explicit delays, and Cause-based error extraction.
OIDC request and session effects
packages/oidc-client/src/lib/...
Authorization, exchange, logout, session, and client-store flows replace Micro execution with Effect execution.
Result-based parsing and validation
packages/journey-client/src/lib/..., packages/sdk-utilities/src/lib/config/...
Journey parsing and configuration validation replace Either with Result. Password rules replace Option with Result.

Tooling and HTTP client updates

Layer / File(s) Summary
Workspace and package configuration
pnpm-workspace.yaml, package.json, e2e/mock-api-v2/package.json, tools/*/package.json
Effect and Vitest catalogs are updated. Obsolete platform dependencies are removed.
User service HTTP runtime
tools/user-scripts/src/lib/user-scripts.ts
The user service uses unstable HTTP modules, unified HTTP errors, and explicit Node HTTP client provisioning.
DaVinci API report updates
packages/davinci-client/api-report/*
Generated API reports include updated node and server state unions.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested reviewers: cerebrl

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 68.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: migrating Effect from v3 to v4 beta.
Description check ✅ Passed The description explains the migration, lists package-level changes, and reports verification results, although it omits the template's JIRA Ticket heading.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/effect-v4-migration

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.

@nx-cloud

nx-cloud Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

View your CI Pipeline Execution ↗ for commit 02cb3c2

Command Status Duration Result
nx affected -t build lint test typecheck e2e-ci ❌ Failed 4m 56s View ↗

💡 Dealing with memory or CPU issues? See memory and CPU details with the resource usage add-on ↗.


☁️ Nx Cloud last updated this comment at 2026-08-06 15:03:54 UTC

nx-cloud[bot]

This comment was marked as outdated.

Effect.sleep in v4 beta.103 hangs in Vite-bundled browser environments
due to ClockRef/withFiber/clockWith indirection chain. Replace with
delayMs helper using Effect.callback + plain setTimeout to match v3
Micro.sleep behavior.

Also fix:
- pollStatus: use Effect.runPromiseExit(effect) direct call (not pipe)
- pollStatus: add Cause.squash fallback for defect errors
- password-policy.rules.ts: Result.failVoid → Result.fail(undefined)
nx-cloud[bot]

This comment was marked as outdated.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@e2e/mock-api-v2/src/main.ts`:
- Around line 39-42: Update the SessionLayer definition to use
Layer.provideMerge instead of Layer.provide, so SessionMiddlewareMock is
satisfied while SessionStorage remains exposed for EndSessionHandlerMock through
HandlersLayer and ServicesLayer.
- Around line 78-82: Update the HttpMiddleware.cors configuration to replace the
wildcard allowedOrigins value with the configured trusted localhost/test origin
list or predicate, while preserving credentials support and the existing
allowedMethods and maxAge settings.

In `@e2e/mock-api-v2/src/middleware/Authorization.ts`:
- Around line 35-37: Remove the raw credential value from the logging flow in
Authorization middleware: stop deriving or passing tokenValue from
Redacted.value(credential) to Effect.log, and retain only the non-sensitive
“checking bearer token” metadata for both valid and invalid requests.

In `@e2e/mock-api-v2/src/services/mock-env-helpers/index.ts`:
- Around line 66-72: Update validateCapabilitiesResponse to pass the request
body shape expected by validator, preserving the existing null validation as
appropriate; alternatively, change validator’s matching pattern to accept the
extracted formData.value shape, ensuring invalid credentials reach Match.orElse
and fail correctly.

In `@e2e/mock-api-v2/src/services/session.service.ts`:
- Line 19: Update refreshSession to expose Error failures instead of declaring
never failures, using the required Effect type with SessionData success and
Error failure channels. In its Effect.fn implementation, yield failed effects so
missing or expired sessions propagate as failures, and return the refreshed
session only on success.

In `@packages/davinci-client/src/lib/client.store.effects.ts`:
- Around line 256-281: Document that pollRetries represents total polling
attempts because doPoll executes once before the loop, with values 0 and 1 both
producing only the initial poll; add boundary tests covering 0, 1, and 2 using
the polling function and existing test setup. If the intended contract is
retries after the initial attempt instead, rename the configuration and adjust
the loop semantics consistently.

In `@packages/oidc-client/src/lib/authorize.request.utils.test.ts`:
- Around line 140-143: Replace the early returns after Cause.findErrorOption in
all listed sites with assertions that errorOpt is Some, then abort before
accessing errorOpt.value when the assertion fails. Apply this to
authorize.request.utils.test.ts ranges 140-143, 187-190, 211-214, and 290-293;
logout.request.test.ts ranges 213-216, 251-254, 321-324, and 360-363; and
authorize.request.micros.test.ts ranges 81-84, 110-113, 145-148, 199-202,
229-232, 240-243, 282-285, 307-310, 334-337, 378-381, 419-422, and 441-444,
preserving the subsequent typed-error validation.

In `@packages/oidc-client/src/lib/session.micros.test.ts`:
- Around line 204-208: Update each failure assertion block in the session tests,
including the blocks using Cause.findErrorOption, to assert
Option.isSome(errorOpt) before the early-return guard. Keep the existing
typed-error value assertions, but ensure a missing error option fails the test
rather than allowing the guard to pass.

In `@tools/user-scripts/src/lib/user-scripts.ts`:
- Around line 96-98: Enable or add active tests in user-scripts.test.ts covering
deleteUser with configuration-provided success behavior and HttpClientError
conversion to DeleteUserError. Keep the tests uncommented and assert both the
successful result and the mapped error outcome.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3bf9e131-5861-45d4-9990-1a2fd9280d3a

📥 Commits

Reviewing files that changed from the base of the PR and between d65f42a and 0f15944.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (58)
  • e2e/mock-api-v2/package.json
  • e2e/mock-api-v2/src/handlers/authorize.handler.ts
  • e2e/mock-api-v2/src/handlers/capabilities.handler.ts
  • e2e/mock-api-v2/src/handlers/end-session.handler.ts
  • e2e/mock-api-v2/src/handlers/healthcheck.handler.ts
  • e2e/mock-api-v2/src/handlers/open-id-configuration.handler.ts
  • e2e/mock-api-v2/src/handlers/revoke.handler.ts
  • e2e/mock-api-v2/src/handlers/token.handler.ts
  • e2e/mock-api-v2/src/handlers/userinfo.handler.ts
  • e2e/mock-api-v2/src/helpers/match.ts
  • e2e/mock-api-v2/src/main.ts
  • e2e/mock-api-v2/src/middleware/Authorization.ts
  • e2e/mock-api-v2/src/middleware/CookieMiddleware.ts
  • e2e/mock-api-v2/src/middleware/Session.ts
  • e2e/mock-api-v2/src/schemas/authorize.schema.ts
  • e2e/mock-api-v2/src/schemas/capabilities/capabilities.request.schema.ts
  • e2e/mock-api-v2/src/schemas/capabilities/capabilities.response.schema.ts
  • e2e/mock-api-v2/src/schemas/open-id-configuration/open-id-configuration-response.schema.ts
  • e2e/mock-api-v2/src/schemas/return-success-response-redirect.schema.ts
  • e2e/mock-api-v2/src/schemas/revoke/revoke.schema.ts
  • e2e/mock-api-v2/src/schemas/token/token.schema.ts
  • e2e/mock-api-v2/src/services/mock-env-helpers/index.ts
  • e2e/mock-api-v2/src/services/session.service.ts
  • e2e/mock-api-v2/src/services/tokens.service.ts
  • e2e/mock-api-v2/src/services/userinfo.service.ts
  • e2e/mock-api-v2/src/spec.ts
  • package.json
  • packages/davinci-client/api-report/davinci-client.api.md
  • packages/davinci-client/api-report/davinci-client.types.api.md
  • packages/davinci-client/src/lib/client.store.effects.test.ts
  • packages/davinci-client/src/lib/client.store.effects.ts
  • packages/davinci-client/src/lib/client.store.ts
  • packages/davinci-client/src/lib/fido/fido.ts
  • packages/davinci-client/src/lib/password-policy.rules.ts
  • packages/journey-client/src/lib/client.store.ts
  • packages/journey-client/src/lib/journey.utils.test.ts
  • packages/journey-client/src/lib/journey.utils.ts
  • packages/oidc-client/src/lib/authorize.request.micros.test.ts
  • packages/oidc-client/src/lib/authorize.request.micros.ts
  • packages/oidc-client/src/lib/authorize.request.ts
  • packages/oidc-client/src/lib/authorize.request.utils.test.ts
  • packages/oidc-client/src/lib/client.store.ts
  • packages/oidc-client/src/lib/exchange.request.ts
  • packages/oidc-client/src/lib/exchange.utils.test.ts
  • packages/oidc-client/src/lib/exchange.utils.ts
  • packages/oidc-client/src/lib/logout.request.test.ts
  • packages/oidc-client/src/lib/logout.request.ts
  • packages/oidc-client/src/lib/session.micros.test.ts
  • packages/oidc-client/src/lib/session.micros.ts
  • packages/sdk-utilities/src/lib/config/config.effects.ts
  • packages/sdk-utilities/src/lib/config/config.test.ts
  • packages/sdk-utilities/src/lib/config/config.types.ts
  • packages/sdk-utilities/src/lib/config/config.utils.ts
  • packages/sdk-utilities/src/lib/micro.utils.ts
  • pnpm-workspace.yaml
  • tools/release/package.json
  • tools/user-scripts/package.json
  • tools/user-scripts/src/lib/user-scripts.ts
💤 Files with no reviewable changes (2)
  • tools/user-scripts/package.json
  • tools/release/package.json

Comment on lines +39 to +42
const SessionLayer = Layer.provide(
SessionMiddlewareMock,
Layer.effect(SessionStorage, SessionStorage.make),
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline e2e/mock-api-v2/src/main.ts --items all

rg -n -C 6 'SessionStorage|SessionLayer|Layer\.provide|Layer\.provideMerge|Layer\.launch' \
  e2e/mock-api-v2/src/main.ts

rg -n -C 4 'yield\*\s+SessionStorage' \
  e2e/mock-api-v2/src/handlers --glob '*.ts'

Repository: ForgeRock/ping-javascript-sdk

Length of output: 5674


🌐 Web query:

effect 2.4 Layer.provideMerge Layer.provide outputs consumed semantics

💡 Result:

In Effect, Layer.provide and Layer.provideMerge are both combinators used for composing layers, but they differ in how they handle the resulting service environment [1][2][3]. Layer.provide Use Layer.provide when you want to satisfy a layer's internal dependencies using another layer without exposing those dependencies to the rest of the application [2][4]. Semantics: It wires the provided layer to satisfy the requirements of the base layer [4]. Once satisfied, those dependencies are "consumed" internally, meaning they are no longer available in the context of the resulting layer [2][4]. This is ideal for encapsulating implementation details [2]. Layer.provideMerge Use Layer.provideMerge when you want to satisfy a layer's dependencies while also keeping those dependencies available for use elsewhere in your program [4][3]. Semantics: It wires the provided layer to satisfy the requirements of the base layer, but crucially, it merges the output of the provided layer into the output of the resulting layer [2][3]. As a result, both the original services and the provided services remain accessible in the final context [4][3]. This is particularly common in testing scenarios where you need access to multiple services simultaneously [4]. Summary Comparison | Method | Deps Satisfied | Available to Program | Use When | |--------|---------------|---------------------|----------| | Layer.provide | Yes | No | Internal dependency satisfaction / Encapsulation [2][4] | | Layer.provideMerge | Yes | Yes | Exposing multiple services / Testing [4][3] | In essence, while both methods fulfill requirements, Layer.provide acts as a form of private dependency injection, whereas Layer.provideMerge acts as a union of available services [2][4][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "provideMerge|provide:" node_modules effect 2>/dev/null || true

Repository: ForgeRock/ping-javascript-sdk

Length of output: 167


Expose SessionStorage from SessionLayer.

Layer.provide satisfies SessionMiddlewareMock and consumes the SessionStorage output, so HandlersLayerServicesLayer has no provider for yield* SessionStorage in EndSessionHandlerMock. Use Layer.provideMerge here to satisfy the middleware and keep SessionStorage available to handlers.

Proposed fix
-const SessionLayer = Layer.provide(
+const SessionLayer = Layer.provideMerge(
   SessionMiddlewareMock,
   Layer.effect(SessionStorage, SessionStorage.make),
 );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const SessionLayer = Layer.provide(
SessionMiddlewareMock,
Layer.effect(SessionStorage, SessionStorage.make),
);
const SessionLayer = Layer.provideMerge(
SessionMiddlewareMock,
Layer.effect(SessionStorage, SessionStorage.make),
);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/mock-api-v2/src/main.ts` around lines 39 - 42, Update the SessionLayer
definition to use Layer.provideMerge instead of Layer.provide, so
SessionMiddlewareMock is satisfied while SessionStorage remains exposed for
EndSessionHandlerMock through HandlersLayer and ServicesLayer.

Comment on lines +78 to 82
HttpMiddleware.cors({
allowedMethods: ['GET', 'PUT', 'POST', 'OPTIONS'],
allowedOrigins: ['*'],
credentials: true,
maxAge: 3600,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In effect@4.0.0-beta.103, does HttpMiddleware.cors({ allowedOrigins: [""] })match arbitrary origins or only an Origin header equal to? Cite the source implementation or API reference.

💡 Result:

In Effect 4.0.0-beta.103, providing allowedOrigins: ["*"] in HttpMiddleware.cors() does not restrict access to origins that literally equal the string * [1][2]. Instead, the implementation treats this as a specific configuration that allows the asterisk character, which browsers interpret as a wildcard allowing all origins [1][2]. According to the official implementation and documentation, allowedOrigins can be an array of strings, a predicate function, or empty (which defaults to allowing all origins) [1][2]. If you provide ["*"], the middleware includes this in the Access-Control-Allow-Origin header for incoming requests, effectively enabling a wildcard CORS policy [3]. Source Implementation API Reference: The relevant implementation is found in the Effect Platform package's HttpMiddleware.ts file [1][2]. The cors constructor handles allowedOrigins by validating the incoming Origin header against the provided configuration (either by checking if the origin is included in the array or by executing the provided predicate) [1][3]. Relevant documentation excerpt from source: @param options.allowedOrigins - Origins allowed to access the resource. Can be: - An array of origin strings (e.g., ["https://example.com", "https://api.example.com"]) - A predicate function to dynamically allow origins - If empty array (default): allows all origins with Access-Control-Allow-Origin: * [1][2]

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the affected file and inspect package versions / HttpMiddleware.cors usage.
printf 'Files:\n'
git ls-files | rg '(^|/)package.json$|e2e/mock-api-v2/src/main.ts|HttpMiddleware' || true

printf '\nAffected file context:\n'
sed -n '1,140p' e2e/mock-api-v2/src/main.ts

printf '\nRoot/package effect versions:\n'
node - <<'JS'
const fs = require('fs');
for (const p of ['package.json','package-lock.json','pnpm-lock.yaml','yarn.lock']) {
  if (fs.existsSync(p)) {
    console.log(`\n--- ${p} ---`);
    const s = fs.readFileSync(p, 'utf8');
    if (p.endsWith('.json')) {
      const j = JSON.parse(s);
      if (j.dependencies?.effect || j.devDependencies?.effect || j.dependencies?.'`@effect/platform`' || j.devDependencies?.'`@effect/platform`') {
        console.log(JSON.stringify({deps:j.dependencies, devDeps:j.devDependencies}, null, 2).slice(0, 2000));
      }
    } else {
      for (const m of ['effect','`@effect/platform`']) {
        const idx = s.indexOf(m + '@');
        console.log(m, idx >= 0 ? s.slice(idx, idx + 200) : 'not found');
      }
    }
  }
}
JS

printf '\nLockfile effect platform refs:\n'
rg -n '"?effect( |-|@)?/?platform"?|effect@.*4\.0\.0-beta\.103|HttpMiddleware\.cors|allowedOrigins' package* e2e -S || true

Repository: ForgeRock/ping-javascript-sdk

Length of output: 5774


🏁 Script executed:

#!/bin/bash
set -fu

printf 'Root/package effect version references:\n'
rg -n --no-heading '"?effect( |-|@)?/?platform"?|`@effect/platform`|effect@|HttpMiddleware\.cors|allowedOrigins' package.json e2e/mock-api-v2/package.json packages/*/package.json e2e/*/package.json -S || true

printf '\nPackage json versions if present:\n'
node - <<'JS'
const fs = require('fs');
const files = ['package.json','e2e/mock-api-v2/package.json'];
for (const f of files) {
  if (!fs.existsSync(f)) continue;
  const j = JSON.parse(fs.readFileSync(f, 'utf8'));
  console.log(`\n--- ${f} ---`);
  console.log(JSON.stringify({ dependencies: j.dependencies, devDependencies: j.devDependencies }, null, 2));
}
JS

printf '\nSource availability check for effect package files:\n'
for d in node_modules/effect node_modules/@effect/platform node_modules/cache?; do
  if [ -d "$d" ]; then echo "$d"; find "$d" -maxdepth 2 -type f | head -30; fi
done

Repository: ForgeRock/ping-javascript-sdk

Length of output: 3404


🌐 Web query:

Effect.ts unstable HttpMiddleware allowedOrigins "*" Origin header exact match

💡 Result:

In Effect.ts, the HttpMiddleware.cors implementation handles the allowedOrigins configuration by performing an exact match when an array of strings is provided [1][2]. Specifically, the middleware logic determines whether to allow an incoming Origin header based on the allowedOrigins option [1][2]: 1. If allowedOrigins is a ReadonlyArray<string>, the middleware checks if the incoming Origin header is included in that array [1][2]. 2. When the array contains exactly one origin, the middleware sets the Access-Control-Allow-Origin header to that specific origin [1][2]. 3. When the array contains multiple origins, or when a predicate function is provided, the middleware dynamically checks the Origin header and, if matched, reflects it back in the Access-Control-Allow-Origin header along with a Vary: Origin header [1][2]. 4. If allowedOrigins is not provided (or is an empty array), the middleware defaults to allowing all origins by setting Access-Control-Allow-Origin: * [3][1][2]. The use of Array.prototype.includes ensures that the comparison against the provided list of origins is an exact string match [1][2]. If you require more flexible matching (e.g., regex or suffix matching), you should provide a predicate function to the allowedOrigins option instead [3][4][5].

Citations:


🌐 Web query:

Effect.ts HttpMiddleware coredocs CORS allowedOrigins asterisk browser Origin header literal "*"

💡 Result:

In the Effect.ts platform library, the CORS middleware is configured via HttpMiddleware.cors [1][2]. According to the official documentation, if the allowedOrigins option is left as an empty array (which is the default behavior), the middleware will allow all origins by setting the Access-Control-Allow-Origin response header to the wildcard value * [2][3]. Regarding the browser's handling of the wildcard * in the Origin header or CORS response: 1. Functionality: The Access-Control-Allow-Origin: * header instructs the browser that any origin may access the resource, provided the request does not include credentials (such as cookies, authorization headers, or TLS client certificates) [4][5]. 2. Credentials Constraint: If a request includes credentials, the browser will block the response if the server returns Access-Control-Allow-Origin: * [5][6]. In such cases, the server must instead specify an explicit, single origin (e.g., Access-Control-Allow-Origin: https://example.com) [4][5][7]. 3. Security: Using the * wildcard is appropriate only for public, non-credentialed resources [8][6]. If your application requires credentialed access, you must configure allowedOrigins to a specific list or a predicate function to dynamically validate the Origin request header against your allowlist [1][2][5]. When using a dynamic predicate for allowedOrigins in Effect.ts, it is recommended practice to include the Vary: Origin response header to inform browsers that the response depends on the Origin request header [4][5].

Citations:


Configure trusted localhost origins instead of ['*'].

allowedOrigins: ['*'] only matches requests whose Origin header is literal *, so browser origins such as Vite development origins will not receive Access-Control-Allow-Origin. This disables browser CORS access when credentials: true. Use a configured test origin list or predicate instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/mock-api-v2/src/main.ts` around lines 78 - 82, Update the
HttpMiddleware.cors configuration to replace the wildcard allowedOrigins value
with the configured trusted localhost/test origin list or predicate, while
preserving credentials support and the existing allowedMethods and maxAge
settings.

Comment on lines 35 to 37
Effect.gen(function* () {
const tokenValue = Redacted.value(bearerToken);
const tokenValue = Redacted.value(credential);
yield* Effect.log('checking bearer token', tokenValue);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not log the raw bearer token.

Redacted.value(credential) exposes the credential. Line 37 then writes it to logs for valid and invalid requests. Remove tokenValue from this log entry. Log only non-sensitive metadata.

Proposed fix
-          yield* Effect.log('checking bearer token', tokenValue);
+          yield* Effect.log('checking bearer token');
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/mock-api-v2/src/middleware/Authorization.ts` around lines 35 - 37, Remove
the raw credential value from the logging flow in Authorization middleware: stop
deriving or passing tokenValue from Redacted.value(credential) to Effect.log,
and retain only the non-sensitive “checking bearer token” metadata for both
valid and invalid requests.

Comment on lines 66 to +72
const validateCapabilitiesResponse = (body: any) =>
pipe(
body,
Option.fromNullable,
Option.map((body) => body.parameters),
Option.map((parameters) => parameters.data),
Option.map((data) => data.formData),
Option.map((formData) => formData.value),
Effect.flatMap(validator),
);
Effect.gen(function* () {
if (body == null) return yield* Effect.fail(new HttpApiError.InternalServerError());
const value = body?.parameters?.data?.formData?.value;
if (value == null) return yield* Effect.fail(new HttpApiError.InternalServerError());
return yield* validator(value);
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Pass the request body shape that validator matches.

Line 69 extracts formData.value, but validator matches the outer parameters.data.formData.username and parameters.data.formData.password structure. The match falls through to Match.orElse, so invalid credentials succeed.

Pass body to validator, or change validator to match the extracted value shape.

Proposed fix
 const validateCapabilitiesResponse = (body: any) =>
   Effect.gen(function* () {
     if (body == null) return yield* Effect.fail(new HttpApiError.InternalServerError());
-    const value = body?.parameters?.data?.formData?.value;
-    if (value == null) return yield* Effect.fail(new HttpApiError.InternalServerError());
-    return yield* validator(value);
+    if (body?.parameters?.data?.formData == null) {
+      return yield* Effect.fail(new HttpApiError.InternalServerError());
+    }
+    return yield* validator(body);
   });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const validateCapabilitiesResponse = (body: any) =>
pipe(
body,
Option.fromNullable,
Option.map((body) => body.parameters),
Option.map((parameters) => parameters.data),
Option.map((data) => data.formData),
Option.map((formData) => formData.value),
Effect.flatMap(validator),
);
Effect.gen(function* () {
if (body == null) return yield* Effect.fail(new HttpApiError.InternalServerError());
const value = body?.parameters?.data?.formData?.value;
if (value == null) return yield* Effect.fail(new HttpApiError.InternalServerError());
return yield* validator(value);
});
const validateCapabilitiesResponse = (body: any) =>
Effect.gen(function* () {
if (body == null) return yield* Effect.fail(new HttpApiError.InternalServerError());
if (body?.parameters?.data?.formData == null) {
return yield* Effect.fail(new HttpApiError.InternalServerError());
}
return yield* validator(body);
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/mock-api-v2/src/services/mock-env-helpers/index.ts` around lines 66 - 72,
Update validateCapabilitiesResponse to pass the request body shape expected by
validator, preserving the existing null validation as appropriate;
alternatively, change validator’s matching pattern to accept the extracted
formData.value shape, ensuring invalid credentials reach Match.orElse and fail
correctly.

sessionId: string,
data: SessionData,
) => Effect.Effect<SessionData | Error, never, never>;
refreshSession: (sessionId: string, expiryDate?: Date) => Effect.Effect<unknown, never, never>;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 5 'refreshSession\s*:|refreshSession\s*\(' e2e/mock-api-v2/src
rg -n -C 5 'return\s+Effect\.fail|return\s+new Error' e2e/mock-api-v2/src/services/session.service.ts

Repository: ForgeRock/ping-javascript-sdk

Length of output: 3893


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== session service relevant lines =="
cat -n e2e/mock-api-v2/src/services/session.service.ts | sed -n '1,115p'

echo
echo "== Session middleware relevant lines =="
cat -n e2e/mock-api-v2/src/middleware/Session.ts | sed -n '1,90p'

echo
echo "== package effect version =="
for f in package.json e2e/mock-api-v2/package.json; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    jq '.dependencies.effect // .devDependencies.effect // empty' "$f"
  fi
done

echo
echo "== lockfile effect entries (focused) =="
for f in pnpm-lock.yaml package-lock.json yarn.lock; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    rg -n '"?effect"?|version:.*effect|/effect@' "$f" | head -80
  fi
done

Repository: ForgeRock/ping-javascript-sdk

Length of output: 10513


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== pnpm catalog/depecks declarations =="
rg -n -C 3 'catalog:|effect:|catalog' package.json pnpm-workspace.yaml pnpm-lock.yaml | sed -n '1,120p'

echo
echo "== lockfile effect 4 version record =="
rg -n -C 5 'effect@4\.0\.0-beta\.103' pnpm-lock.yaml

echo
echo "== installed effect package metadata if present =="
node - <<'JS'
const fs = require('fs');
const paths = ['e2e/mock-api-v2/node_modules/effect/package.json','node_modules/effect/package.json','packages/sdk-effects/node_modules/effect/package.json'];
for (const path of paths) {
  if (fs.existsSync(path)) {
    const pkg = JSON.parse(fs.readFileSync(path,'utf8'));
    console.log(path, {version(pkg),main(pkg),types: pkg.types});
    break;
  }
}
console.log('installed_effect=' + paths.find(p => fs.existsSync(p) || ''));
JS

Repository: ForgeRock/ping-javascript-sdk

Length of output: 13361


🌐 Web query:

Effect v4 4.0.0-beta.103 Effect.fn generator return Effect fail yield* nested Effect warning

💡 Result:

The warning you are encountering regarding nested Effect.gen calls is triggered by the nestedEffectGenYield diagnostic provided by the @effect/language-service package [1][2][3]. This diagnostic is designed to warn developers when they yield a bare Effect.gen inside an existing Effect generator context [2]. The rationale for this warning is that such nested generators can typically be inlined directly into the parent Effect generator, which simplifies the code and improves readability [2]. If you are seeing this warning in a context where you believe it is unnecessary (e.g., in certain single-return patterns), note that recent updates have addressed false positives [4]. Specifically, pull request #746 in the @effect/language-service repository avoids reporting the unnecessaryEffectGen warning when the yielded expression contains a nested yield* [4]. Key points regarding this diagnostic: - It is identified as nestedEffectGenYield [1][2]. - It is supported for both Effect v3 and v4 [2]. - Its default severity is set to off in some configurations, meaning you can choose to enable or disable it based on your project's coding standards [2][5]. - You can find documentation on how to configure or disable specific diagnostics in the @effect/language-service documentation [5][6]. If your current environment (using 4.0.0-beta.103) is reporting this and you find it disruptive, ensure your @effect/language-service package is updated to the latest version that includes the fix for false positives [4][3].

Citations:


🌐 Web query:

effect 4.0.0-beta.103 Effect.orDie documentation

💡 Result:

In Effect 4.0.0-beta.103, Effect.orDie is a function used to convert an effect's expected error into an unrecoverable defect, effectively removing the error from the effect's type [1]. When applied, any failure that occurs within that effect will cause the fiber to terminate [1]. Key aspects of Effect.orDie: - Purpose: It is intended for scenarios where an error is considered unrecoverable and you do not wish to perform error handling within the effect pipeline [1]. - Type transformation: It changes an Effect<A, E, R> into an Effect<A, never, R> [1]. - Behavior: By converting the failure into a defect (fiber termination), it simplifies types for cases where you prefer failures to bubble up rather than being handled locally [1][2]. Usage note: In the transition to Effect v4, some associated utility functions have changed. For example, Effect.orDieWith has been removed; it is recommended to use a combination of Effect.mapError and Effect.orDie instead [3]: myEffect.pipe( Effect.mapError(e => new Error(Failed: ${e})), Effect.orDie); This function is also available within the Micro module (Micro.orDie), which follows the same principle of elevating expected errors to unexpected ones [4][5].

Citations:


Restore refreshSession failure propagation.

refreshSession declares never failures, but the implementation hides refresh failures with successful values. Effect.fail(...) and new Error(...) inside Effect.fn are returned as success values unless yielded/frowned as the failed Effect. Callers then treat missing/expired sessions as successful refreshes and use Effect.orDie on the wrong value.

Type failures with Effect.Effect<SessionData | SessionData | Error, Error, never>. Return failed refreshes with yield* Effect.fail(...). Return the refreshed session on success.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/mock-api-v2/src/services/session.service.ts` at line 19, Update
refreshSession to expose Error failures instead of declaring never failures,
using the required Effect type with SessionData success and Error failure
channels. In its Effect.fn implementation, yield failed effects so missing or
expired sessions propagate as failures, and return the refreshed session only on
success.

Comment on lines +256 to +281
}): Effect.Effect<PollingStatus, InternalErrorResponse> {
const maxRetries = collector.output.config.pollRetries ?? 60;
const pollInterval = collector.output.config.pollInterval ?? 2000;

return validatePollingPrerequisitesµ(store.getState(), challenge).pipe(
Micro.flatMap(({ interactionId, challengeEndpoint }) =>
Micro.promise(() =>
return Effect.gen(function* () {
const { interactionId, challengeEndpoint } = yield* validatePollingPrerequisitesµ(
store.getState(),
challenge,
);

const doPoll = (): Effect.Effect<PollDispatchResult, never> =>
Effect.promise(() =>
store.dispatch(
davinciApi.endpoints.poll.initiate({
endpoint: challengeEndpoint,
interactionId,
}),
),
),
),
Micro.repeat({
while: isChallengeStillPending,
// `times` tracks repetitions after the initial attempt, so decrement by one
times: maxRetries - 1,
schedule: Micro.scheduleSpaced(pollInterval),
}),
Micro.map((response) => interpretChallengeResponse(response, log)),
Micro.flatMap((result) =>
isInternalError(result) ? Micro.fail(result) : Micro.succeed(result),
),
);
);

let response: PollDispatchResult = yield* doPoll();

for (let i = 0; i < maxRetries - 1 && isChallengeStillPending(response); i++) {
yield* delayMs(pollInterval);
response = yield* doPoll();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the polling configuration contract and its test coverage.
rg -n -C 4 '\bpollRetries\b|\bpollInterval\b' packages e2e tools
rg -n -C 8 'pollStatus|timedOut|isChallengeStillPending|challengePolling' \
  --glob '*test.*' packages/davinci-client

Repository: ForgeRock/ping-javascript-sdk

Length of output: 32668


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Effect implementation around pollRetries:"
sed -n '220,330p' packages/davinci-client/src/lib/client.store.effects.ts

echo
echo "All dispatch polling tests:"
rg -n -C 12 'describe|it|pollRetries|timedOut|doPoll|dispatch|initiate' packages/davinci-client/src/lib/client.store.effects.test.ts

echo
echo "Other pollRetries tests mentioning attempted poll counts:"
rg -n -C 8 'pollRetries|attempt|poll.*count|count|timedOut' e2e packages/davinci-client/src --glob '*test.*' \
  | sed -n '1,260p'

echo
echo "Programmatic loop count behavior for pending responses:"
python3 - <<'PY'
def loop(maxRetries):
    count = 1
    i = 0
    while i < maxRetries - 1:
        count += 1
        i += 1
    return count

for mr in [-1, 0, 1, 2, 5, 60]:
    print(mr, "=>", loop(mr))
PY

Repository: ForgeRock/ping-javascript-sdk

Length of output: 35112


Document pollRetries boundary behavior.

The client dispatches once before the retry loop, so pollRetries controls total polling attempts and 0/1 both result in only the initial poll. Add boundary tests for 0, 1, and 2, or rename the config if it should mean retries after the initial attempt.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/davinci-client/src/lib/client.store.effects.ts` around lines 256 -
281, Document that pollRetries represents total polling attempts because doPoll
executes once before the loop, with values 0 and 1 both producing only the
initial poll; add boundary tests covering 0, 1, and 2 using the polling function
and existing test setup. If the intended contract is retries after the initial
attempt instead, rename the configuration and adjust the loop semantics
consistently.

Comment on lines +140 to +143
expect(Exit.isFailure(result)).toBe(true);
if (!Exit.isFailure(result)) return;
const errorOpt = Cause.findErrorOption(result.cause);
if (!Option.isSome(errorOpt)) return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -U --pcre2 \
  'const errorOpt = Cause\.findErrorOption\([^;]+;\s*\n\s*if \(!Option\.isSome\(errorOpt\)\) return;' \
  packages/oidc-client/src/lib \
  -g '*.test.ts'

Repository: ForgeRock/ping-javascript-sdk

Length of output: 8531


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Candidate dependency/version files =="
git ls-files | rg '(^|/)(package.json|pnpm-lock.yaml|yarn.lock|package-lock.json)$' || true

echo
echo "== Locate Cause.findErrorOption definitions/usages =="
rg -n "findErrorOption|interface Cause|type Cause|Cause:" packages/oidc-client/src -g '*.ts' | head -200

echo
echo "== Inspect authorize.request.utils.test around listed sites =="
for f in packages/oidc-client/src/lib/authorize.request.utils.test.ts packages/oidc-client/src/lib/logout.request.test.ts packages/oidc-client/src/lib/authorize.request.micros.test.ts; do
  echo "--- $f ---"
  wc -l "$f"
  sed -n '1,320p' "$f"
done

Repository: ForgeRock/ping-javascript-sdk

Length of output: 40129


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== effect dependency version =="
node - <<'JS'
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('packages/oidc-client/package.json', 'utf8'));
console.log(pkg.dependencies?.effect ?? pkg.devDependencies?.effect ?? pkg.peerDependencies?.effect ?? 'not found');
JS

echo
echo "== pnpm-lock effect entries =="
rg -n " packages/effect|/effect:" pnpm-lock.yaml | head -100 || true

echo
echo "== Locate Effect error/fail implementations =="
rg -n "function .*error|const .*error|class .*Error|isEffectError|isCause|Cause\\.find|TypedError" node_modules/.pnpm 2>/dev/null | head -200 || true

Repository: ForgeRock/ping-javascript-sdk

Length of output: 294


🌐 Web query:

Effect findErrorOption Cause findErrorOption Effect defect effect 3.x

💡 Result:

In the Effect library, findErrorOption is a function used to extract the first typed error (a "Fail" reason) from a Cause or an Exit [1][2][3][4]. It returns an Option: Option.some(error) if a "Fail" reason exists, and Option.none() if it does not [1][3][4]. Regarding your query about findErrorOption and defects: - When a Cause contains a defect (e.g., created via Cause.die), findErrorOption will return Option.none() [1][3][4]. It is specifically designed to isolate typed errors and ignores other types of failures such as defects or interruptions [2][3][4]. - In Effect 3.x, Cause.failureOption was the equivalent API [5]. This was renamed to findErrorOption in Effect 4.0.0 [2][5][3]. If you need to find defects specifically, you should use findDefect or findDefectOption instead of findErrorOption [2][5][3].

Citations:


Assert errorOpt is Some before reading Effect cause values.

Exit.isFailure also matches defects, but Cause.findErrorOption returns None for defects. The current return skips the expected failure validation and lets the test pass when the cause is a typed die.

Replace each early return with an assertion such as expect(Option.isSome(errorOpt)).toBe(true), then throw or abort the test before reading errorOpt.value.

📍 Affects 3 files
  • packages/oidc-client/src/lib/authorize.request.utils.test.ts#L140-L143 (this comment)
  • packages/oidc-client/src/lib/authorize.request.utils.test.ts#L187-L190
  • packages/oidc-client/src/lib/authorize.request.utils.test.ts#L211-L214
  • packages/oidc-client/src/lib/authorize.request.utils.test.ts#L290-L293
  • packages/oidc-client/src/lib/logout.request.test.ts#L213-L216
  • packages/oidc-client/src/lib/logout.request.test.ts#L251-L254
  • packages/oidc-client/src/lib/logout.request.test.ts#L321-L324
  • packages/oidc-client/src/lib/logout.request.test.ts#L360-L363
  • packages/oidc-client/src/lib/authorize.request.micros.test.ts#L81-L84
  • packages/oidc-client/src/lib/authorize.request.micros.test.ts#L110-L113
  • packages/oidc-client/src/lib/authorize.request.micros.test.ts#L145-L148
  • packages/oidc-client/src/lib/authorize.request.micros.test.ts#L199-L202
  • packages/oidc-client/src/lib/authorize.request.micros.test.ts#L229-L232
  • packages/oidc-client/src/lib/authorize.request.micros.test.ts#L240-L243
  • packages/oidc-client/src/lib/authorize.request.micros.test.ts#L282-L285
  • packages/oidc-client/src/lib/authorize.request.micros.test.ts#L307-L310
  • packages/oidc-client/src/lib/authorize.request.micros.test.ts#L334-L337
  • packages/oidc-client/src/lib/authorize.request.micros.test.ts#L378-L381
  • packages/oidc-client/src/lib/authorize.request.micros.test.ts#L419-L422
  • packages/oidc-client/src/lib/authorize.request.micros.test.ts#L441-L444
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/oidc-client/src/lib/authorize.request.utils.test.ts` around lines
140 - 143, Replace the early returns after Cause.findErrorOption in all listed
sites with assertions that errorOpt is Some, then abort before accessing
errorOpt.value when the assertion fails. Apply this to
authorize.request.utils.test.ts ranges 140-143, 187-190, 211-214, and 290-293;
logout.request.test.ts ranges 213-216, 251-254, 321-324, and 360-363; and
authorize.request.micros.test.ts ranges 81-84, 110-113, 145-148, 199-202,
229-232, 240-243, 282-285, 307-310, 334-337, 378-381, 419-422, and 441-444,
preserving the subsequent typed-error validation.

Comment on lines +204 to +208
expect(Exit.isFailure(exit)).toBe(true);
if (!Exit.isFailure(exit)) return;
const errorOpt = Cause.findErrorOption(exit.cause);
if (!Option.isSome(errorOpt)) return;
expect(errorOpt.value.error).toBe('no_id_token_hint');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert that each failed Effect has a typed error.

At Line 207 and the equivalent blocks, the guard returns when Cause.findErrorOption is None. The test then passes if the Effect dies or is interrupted instead of failing with the expected GenericError.

Add an Option.isSome assertion before each guard.

Proposed fix
 const errorOpt = Cause.findErrorOption(exit.cause);
+expect(Option.isSome(errorOpt)).toBe(true);
 if (!Option.isSome(errorOpt)) return;

Also applies to: 247-251, 277-281, 316-320, 339-343, 363-367, 387-391, 441-445, 461-465, 487-491, 521-525, 574-578, 596-600, 652-656, 665-669, 681-685, 698-702, 713-717

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/oidc-client/src/lib/session.micros.test.ts` around lines 204 - 208,
Update each failure assertion block in the session tests, including the blocks
using Cause.findErrorOption, to assert Option.isSome(errorOpt) before the
early-return guard. Keep the existing typed-error value assertions, but ensure a
missing error option fails the test rather than allowing the guard to pass.

Comment on lines +96 to +98
export const UserRuntime = ManagedRuntime.make(
Layer.provide(Layer.effect(UserService, UserService.make), NodeHttpClient.layerUndici),
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline tools/user-scripts/src/lib/user-scripts.ts --items all
fd -t f 'user-scripts.*test.*|user-scripts.*spec.*' tools/user-scripts | while IFS= read -r file; do
  echo "=== $file ==="
  rg -n -C 4 'UserRuntime|UserService|deleteUser|HttpClientError|DeleteUserError|ManagedRuntime' "$file"
done

Repository: ForgeRock/ping-javascript-sdk

Length of output: 3711


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== user-scripts.ts relevant section ==="
nl -ba tools/user-scripts/src/lib/user-scripts.ts | sed -n '1,160p'

echo "=== tracked test files under tools/user-scripts ==="
git ls-files tools/user-scripts | grep -E '(test|spec|vitest|jest)' || true

echo "=== UserRuntime references ==="
rg -n 'UserRuntime|UserService\.DefaultWithoutDependencies|\.deleteUser\(|HttpClientError|DeleteUserError|getUsers' tools/user-scripts || true

echo "=== package/test config ==="
fd -t f 'package.json|vitest.config.*|jest.config.*|tsconfig.*' tools/user-scripts . | sed -n '1,120p'
for f in $(fd -t f 'package.json|vitest.config.*|jest.config.*|tsconfig.*' tools/user-scripts .); do
  echo "--- $f ---"
  sed -n '1,220p' "$f"
done

Repository: ForgeRock/ping-javascript-sdk

Length of output: 248


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== user-scripts.ts relevant section ==="
awk '{printf "%4d\t%s\n", NR, $0}' tools/user-scripts/src/lib/user-scripts.ts | sed -n '1,170p'

echo "=== tracked test files under tools/user-scripts ==="
git ls-files tools/user-scripts | grep -E '(test|spec|vitest|jest)' || true

echo "=== UserRuntime references ==="
rg -n 'UserRuntime|UserService\.DefaultWithoutDependencies|\.deleteUser\(|HttpClientError|DeleteUserError|getUsers' tools/user-scripts || true

echo "=== package/test config ==="
find tools/user-scripts . -maxdepth 3 -type f \( -name 'package.json' -o -name 'vitest.config.*' -o -name 'jest.config.*' -o -name 'tsconfig.*' \) -print | sort | while read -r f; do
  echo "--- $f ---"
  sed -n '1,220p' "$f"
done

Repository: ForgeRock/ping-javascript-sdk

Length of output: 50385


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== git status ==="
git status --short

echo "=== diff for tools/user-scripts ==="
git diff -- tools/user-scripts/src/lib/user-scripts.test.ts tools/user-scripts/src/lib/user-scripts.ts || true

echo "=== user-scripts.test.ts exact relevant block ==="
awk '{printf "%4d\t%s\n", NR, $0}' tools/user-scripts/src/lib/user-scripts.test.ts | sed -n '1,165p'

echo "=== coverage configuration mentions ==="
rg -n 'coverage|user-scripts|tools/user-scripts|tool:.*user-scripts|vitest|enabled' tools/user-scripts/package.json tools/user-scripts/vitest.config.ts package.json nx.json 2>/dev/null || true

Repository: ForgeRock/ping-javascript-sdk

Length of output: 7342


Add uncommented coverage for deleteUser.

tools/user-scripts/src/lib/user-scripts.test.ts still has all tests commented, so no active tests cover config-provided deleteUser success or HttpClientError -> DeleteUserError handling. Enable tests or add new active coverage for these cases.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/user-scripts/src/lib/user-scripts.ts` around lines 96 - 98, Enable or
add active tests in user-scripts.test.ts covering deleteUser with
configuration-provided success behavior and HttpClientError conversion to
DeleteUserError. Keep the tests uncommented and assert both the successful
result and the mapped error outcome.

@nx-cloud nx-cloud Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nx Cloud has identified a possible root cause for your failed CI:

We classified this failure as an environment issue rather than a code change. The PAR E2E test successfully reached the external OIDC server (openam-sdks.forgeblocks.com), but the server never redirected back to the expected localhost callback — a behavior controlled entirely by the external service's client registration, not by our Effect v3 → v4 migration. No changes in this PR affect PAR redirect URIs or the oidc-suites test infrastructure.

No code changes were suggested for this issue.

Trigger a rerun:

Rerun CI

Nx Cloud View detailed reasoning on Nx Cloud ↗


🎓 Learn more about Self-Healing CI on nx.dev

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant