From 60b1047155ae2589c70b350b2c6d58e8d8f0eee1 Mon Sep 17 00:00:00 2001 From: Patrick Roza Date: Thu, 19 Feb 2026 09:49:22 +0100 Subject: [PATCH 01/45] initial, with added repos --- .gitmodules | 6 ++++++ repos/effect | 1 + repos/effect-smol | 1 + 3 files changed, 8 insertions(+) create mode 100644 .gitmodules create mode 160000 repos/effect create mode 160000 repos/effect-smol diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..ed6ad4fdb --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "repos/effect-smol"] + path = repos/effect-smol + url = https://github.com/Effect-TS/effect-smol.git +[submodule "repos/effect"] + path = repos/effect + url = https://github.com/Effect-TS/effect.git diff --git a/repos/effect b/repos/effect new file mode 160000 index 000000000..4f2107548 --- /dev/null +++ b/repos/effect @@ -0,0 +1 @@ +Subproject commit 4f2107548fa64c21a8643b7b0efcd556cd16d4b9 diff --git a/repos/effect-smol b/repos/effect-smol new file mode 160000 index 000000000..1ca2ed673 --- /dev/null +++ b/repos/effect-smol @@ -0,0 +1 @@ +Subproject commit 1ca2ed67301a5dc40ae0ed94346b99f26fd22bbe From a13eb19c55c3ad14eab8abaa29ff6fc7888583c5 Mon Sep 17 00:00:00 2001 From: Patrick Roza Date: Thu, 19 Feb 2026 09:49:32 +0100 Subject: [PATCH 02/45] add task --- task/Migrate_to_Effect_v4.md | 41 ++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 task/Migrate_to_Effect_v4.md diff --git a/task/Migrate_to_Effect_v4.md b/task/Migrate_to_Effect_v4.md new file mode 100644 index 000000000..046eb0e36 --- /dev/null +++ b/task/Migrate_to_Effect_v4.md @@ -0,0 +1,41 @@ +# Migration + +Right now we are using Effect v3 (https://github.com/effect-ts/effect) +The task is about migrating to Effect v4 (https://github.com/effect-ts/effect-smol) + +There are migration guides: +- Announcement: https://effect.website/blog/releases/effect/40-beta/ +- v3 to v4 general: https://github.com/Effect-TS/effect-smol/blob/main/MIGRATION.md +- Schema v3 to v4: https://github.com/Effect-TS/effect-smol/blob/main/packages/effect/SCHEMA.md#migration-from-v3 + +## Approach + +1. Convert `cli` - as it's a standalone utility using minimal effect libraries. +2. Convert `effect-app` core +3. Convert `infra` +4. Convert `vue` +5. Convert `vue-components` + +Each step will be completed individually, and only move on to the next step when the current is done succesfully. +For each step we should find out if we can convert 1:1 or certain things are missing preventing that. + +## Conversion + +1. replace `effect` and `@effect/*` package.json references, with their respective v4 counter parts (most @effect/* have moved into `effect/unstable/*`), rerun `pnpm i` +2. replace `effect` and `@effect/*` typescript references, with their respective v4 counter parts (most @effect/* have moved into `effect/unstable/*`) +3. use new names of v4 functions and modules accordingly + +## Later + +- detect naming patterns we adopted from effect v3 in our libraries, and change them to match v4 naming patterns. + +## Concerns + +- `Effect.Service` has been removed, to ease migration, we may at first copy (and adjust to effect-smol) the original implementation, export it from `effect-app/core/Effect` and replace it at a later stage. + +## Context + +- The effect repo is located inside `./repos/effect` +- The effect-smol repo is located inside `./repos/effect-smol` + +All repos can be kept uptodate with `git submodule foreach git pull origin main` and `git submodule foreach pnpm i`. From 372d5e0e6e54978f41ea5ce13cb656aacf742981 Mon Sep 17 00:00:00 2001 From: Patrick Roza Date: Thu, 19 Feb 2026 09:56:24 +0100 Subject: [PATCH 03/45] update smol --- repos/effect-smol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/effect-smol b/repos/effect-smol index 1ca2ed673..eb2a85ed4 160000 --- a/repos/effect-smol +++ b/repos/effect-smol @@ -1 +1 @@ -Subproject commit 1ca2ed67301a5dc40ae0ed94346b99f26fd22bbe +Subproject commit eb2a85ed4dc162b2535d304799333a5a20477fd0 From 77834f3a1c63c10148d42f9a9cde5cc10a690b16 Mon Sep 17 00:00:00 2001 From: Patrick Roza Date: Thu, 19 Feb 2026 10:05:19 +0100 Subject: [PATCH 04/45] use local refs --- task/Migrate_to_Effect_v4.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/task/Migrate_to_Effect_v4.md b/task/Migrate_to_Effect_v4.md index 046eb0e36..059c536d0 100644 --- a/task/Migrate_to_Effect_v4.md +++ b/task/Migrate_to_Effect_v4.md @@ -1,12 +1,12 @@ # Migration -Right now we are using Effect v3 (https://github.com/effect-ts/effect) -The task is about migrating to Effect v4 (https://github.com/effect-ts/effect-smol) +Right now we are using Effect v3 (/repos/effect) +The task is about migrating to Effect v4 (/repos/effect-smol) There are migration guides: - Announcement: https://effect.website/blog/releases/effect/40-beta/ -- v3 to v4 general: https://github.com/Effect-TS/effect-smol/blob/main/MIGRATION.md -- Schema v3 to v4: https://github.com/Effect-TS/effect-smol/blob/main/packages/effect/SCHEMA.md#migration-from-v3 +- [v3 to v4 general](/repos/effect-smol/MIGRATION.md) +- [Schema v3 to v4](/repos/effect-smol/packages/effect/SCHEMA.md#migration-from-v3) ## Approach @@ -21,11 +21,13 @@ For each step we should find out if we can convert 1:1 or certain things are mis ## Conversion +We start with an as close as possible 1:1 conversion. In future tasks we can worry about refactoring. + 1. replace `effect` and `@effect/*` package.json references, with their respective v4 counter parts (most @effect/* have moved into `effect/unstable/*`), rerun `pnpm i` 2. replace `effect` and `@effect/*` typescript references, with their respective v4 counter parts (most @effect/* have moved into `effect/unstable/*`) 3. use new names of v4 functions and modules accordingly -## Later +## Out of scope - detect naming patterns we adopted from effect v3 in our libraries, and change them to match v4 naming patterns. @@ -35,7 +37,7 @@ For each step we should find out if we can convert 1:1 or certain things are mis ## Context -- The effect repo is located inside `./repos/effect` -- The effect-smol repo is located inside `./repos/effect-smol` +- The effect repo is located inside `/repos/effect` +- The effect-smol repo is located inside `/repos/effect-smol` All repos can be kept uptodate with `git submodule foreach git pull origin main` and `git submodule foreach pnpm i`. From 12782534162e32c3ed9b04c3b3d08e9ef940113f Mon Sep 17 00:00:00 2001 From: Patrick Roza Date: Thu, 19 Feb 2026 10:06:14 +0100 Subject: [PATCH 05/45] instructions --- task/Migrate_to_Effect_v4.md | 1 + 1 file changed, 1 insertion(+) diff --git a/task/Migrate_to_Effect_v4.md b/task/Migrate_to_Effect_v4.md index 059c536d0..2550cf2ae 100644 --- a/task/Migrate_to_Effect_v4.md +++ b/task/Migrate_to_Effect_v4.md @@ -18,6 +18,7 @@ There are migration guides: Each step will be completed individually, and only move on to the next step when the current is done succesfully. For each step we should find out if we can convert 1:1 or certain things are missing preventing that. +Create task files for each, and track progress and findings in each. ## Conversion From 45f7dbfe63b5b47641da1591e36c73352de00568 Mon Sep 17 00:00:00 2001 From: Patrick Roza Date: Thu, 19 Feb 2026 10:09:50 +0100 Subject: [PATCH 06/45] borrow some AGENTS.md --- AGENTS.md | 120 +++++++++++++++++++++++++++ package.json | 2 +- packages/cli/package.json | 2 +- packages/effect-app/package.json | 2 +- packages/infra/package.json | 2 +- packages/vue-components/package.json | 2 +- packages/vue/package.json | 2 +- 7 files changed, 126 insertions(+), 6 deletions(-) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..bf5d7bb57 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,120 @@ +# Agent Instructions + +This is the Effect App library repository, focusing on functional programming patterns and effect systems in TypeScript, extending the Effect library. + +## Development Workflow + +- The git base branch is `main` +- Use `pnpm` as the package manager + +### Core Principles + +- **Zero Tolerance for Errors**: All automated checks must pass +- **Clarity over Cleverness**: Choose clear, maintainable solutions +- **Conciseness**: Keep code and any wording concise and to the point. Sacrifice grammar for the sake of concision. +- **Reduce comments**: Avoid comments unless absolutely required to explain unusual or complex logic. Comments in jsdocs are acceptable. + +### Mandatory Validation Steps + +- Run `pnpm lint-fix` after editing files + +- Run type checking: `pnpm build` + - If type checking continues to fail, run `pnpm clean` to clear caches, then re-run `pnpm check` + + +## Code Style Guidelines + +**Always** look at existing code in the repository to learn and follow +established patterns before writing new code. + +Do not worry about getting code formatting perfect while writing. Use `pnpm lint-fix` +to automatically format code according to the project's style guidelines. + +## Prefer `Effect.fnUntraced` over functions that return `Effect.gen` + +Instead of writing: + +```ts +const fn = (param: string) => + Effect.gen(function*() { + // ... + }) +``` + +Prefer: + +```ts +const fn = Effect.fnUntraced(function*(param: string) { + // ... +}) +``` + +## Using `ServiceMap.Service` + +Prefer the class syntax when working with `ServiceMap.Service`. For example: + +```ts +import { ServiceMap } from "effect" + +class MyService extends ServiceMap.Service number +}>()("MyService") {} +``` + + + + + + + +## Changesets + +All pull requests must include a changeset. You can create changesets in the +`.changeset/` directory. + +The have the following format: + +```md +--- +"package-name": patch | minor | major +--- + +A description of the change. +``` diff --git a/package.json b/package.json index ab52f9da3..fbf2694ef 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "preinstall": "npx only-allow pnpm", "clean": "pnpm all clean", "clean-dist": "pnpm -r clean-dist", - "autofix": "NODE_OPTIONS=--max-old-space-size=6144 pnpm -r --no-bail autofix", + "lint-fix": "NODE_OPTIONS=--max-old-space-size=6144 pnpm -r --no-bail lint-fix", "lint": "pnpm -r lint", "circular:dist": "pnpm -r circular:dist", "test": "pnpm -r test:run", diff --git a/packages/cli/package.json b/packages/cli/package.json index 5d1c51292..9069a6bb9 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -65,7 +65,7 @@ "compile": "NODE_OPTIONS=--max-old-space-size=6144 tsc --noEmit", "lint": "NODE_OPTIONS=--max-old-space-size=6144 ESLINT_TS=1 eslint ./src", "lint:watch": "ESLINT_TS=1 esw -w --changed --clear --ext ts,tsx .", - "autofix": "pnpm lint --fix", + "lint-fix": "pnpm lint --fix", "test": "vitest", "test:run": "pnpm run test run --passWithNoTests", "testsuite": "pnpm lint && pnpm circular && pnpm run test:run", diff --git a/packages/effect-app/package.json b/packages/effect-app/package.json index 3b1f64009..06c8e4d3d 100644 --- a/packages/effect-app/package.json +++ b/packages/effect-app/package.json @@ -297,7 +297,7 @@ "compile": "NODE_OPTIONS=--max-old-space-size=6144 tsc --noEmit", "lint": "NODE_OPTIONS=--max-old-space-size=6144 ESLINT_TS=1 eslint ./src", "lint:watch": "ESLINT_TS=1 esw -w --changed --clear --ext ts,tsx .", - "autofix": "pnpm lint --fix", + "lint-fix": "pnpm lint --fix", "test": "vitest", "test:run": "pnpm run test run --passWithNoTests", "testsuite": "pnpm lint && pnpm circular && pnpm run test:run", diff --git a/packages/infra/package.json b/packages/infra/package.json index 00a4427b1..8860b7c6d 100644 --- a/packages/infra/package.json +++ b/packages/infra/package.json @@ -396,7 +396,7 @@ "compile": "NODE_OPTIONS=--max-old-space-size=6144 tsc --noEmit", "lint": "NODE_OPTIONS=--max-old-space-size=6144 ESLINT_TS=1 eslint ./src", "lint:watch": "ESLINT_TS=1 esw -w --changed --clear --ext ts,tsx .", - "autofix": "pnpm lint --fix", + "lint-fix": "pnpm lint --fix", "test": "vitest", "test:run": "pnpm run test run --passWithNoTests", "testsuite": "pnpm lint && pnpm circular && pnpm run test:run", diff --git a/packages/vue-components/package.json b/packages/vue-components/package.json index 8b0a3c45c..08392f460 100644 --- a/packages/vue-components/package.json +++ b/packages/vue-components/package.json @@ -10,7 +10,7 @@ "lint": "NODE_OPTIONS=--max-old-space-size=8192 eslint src stories .storybook", "ncu": "ncu", "clean": "rm -rf dist", - "autofix": "pnpm lint --fix", + "lint-fix": "pnpm lint --fix", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build", "test": "vitest", diff --git a/packages/vue/package.json b/packages/vue/package.json index 26d737e72..b8d593e87 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -124,7 +124,7 @@ "compile": "NODE_OPTIONS=--max-old-space-size=6144 tsc --noEmit", "lint": "NODE_OPTIONS=--max-old-space-size=6144 ESLINT_TS=1 eslint ./src", "lint:watch": "ESLINT_TS=1 esw -w --changed --clear --ext ts,tsx .", - "autofix": "pnpm lint --fix", + "lint-fix": "pnpm lint --fix", "test": "vitest", "test:run": "pnpm run test run --passWithNoTests", "testsuite": "pnpm lint && pnpm circular && pnpm run test:run", From 009ac6d82eb42e014bdd213db7609f057f938cd1 Mon Sep 17 00:00:00 2001 From: Patrick Roza Date: Thu, 19 Feb 2026 10:10:51 +0100 Subject: [PATCH 07/45] doc --- task/Migrate_to_Effect_v4.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/task/Migrate_to_Effect_v4.md b/task/Migrate_to_Effect_v4.md index 2550cf2ae..735fd39b9 100644 --- a/task/Migrate_to_Effect_v4.md +++ b/task/Migrate_to_Effect_v4.md @@ -20,9 +20,11 @@ Each step will be completed individually, and only move on to the next step when For each step we should find out if we can convert 1:1 or certain things are missing preventing that. Create task files for each, and track progress and findings in each. +Always check `AGENTS.md` in the root of each repository to understand rules. + ## Conversion -We start with an as close as possible 1:1 conversion. In future tasks we can worry about refactoring. +We start with an as close as possible 1:1 conversion. 1. replace `effect` and `@effect/*` package.json references, with their respective v4 counter parts (most @effect/* have moved into `effect/unstable/*`), rerun `pnpm i` 2. replace `effect` and `@effect/*` typescript references, with their respective v4 counter parts (most @effect/* have moved into `effect/unstable/*`) @@ -31,6 +33,7 @@ We start with an as close as possible 1:1 conversion. In future tasks we can wor ## Out of scope - detect naming patterns we adopted from effect v3 in our libraries, and change them to match v4 naming patterns. +- general refactorings and improvementsks ## Concerns From d96d8d6c6dada7a17dc124a25913dd4cc7125a8e Mon Sep 17 00:00:00 2001 From: Patrick Roza Date: Thu, 19 Feb 2026 10:30:38 +0100 Subject: [PATCH 08/45] update instructions --- AGENTS.md | 1 + task/Migrate_to_Effect_v4.md | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index bf5d7bb57..7e90406a7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,6 +13,7 @@ This is the Effect App library repository, focusing on functional programming pa - **Clarity over Cleverness**: Choose clear, maintainable solutions - **Conciseness**: Keep code and any wording concise and to the point. Sacrifice grammar for the sake of concision. - **Reduce comments**: Avoid comments unless absolutely required to explain unusual or complex logic. Comments in jsdocs are acceptable. +- **Never import local `/repos` files**: Always use the latest online versions of packages instead. ### Mandatory Validation Steps diff --git a/task/Migrate_to_Effect_v4.md b/task/Migrate_to_Effect_v4.md index 735fd39b9..a7738e61b 100644 --- a/task/Migrate_to_Effect_v4.md +++ b/task/Migrate_to_Effect_v4.md @@ -20,7 +20,8 @@ Each step will be completed individually, and only move on to the next step when For each step we should find out if we can convert 1:1 or certain things are missing preventing that. Create task files for each, and track progress and findings in each. -Always check `AGENTS.md` in the root of each repository to understand rules. +- Always check `AGENTS.md` in the root of each repository to understand rules. +- You're allowed to use different versions of workspace packages within a project for the duration of the migration. ## Conversion From c205dc19d57fc3dbc6045d6e212cb390746446fd Mon Sep 17 00:00:00 2001 From: Patrick Roza Date: Thu, 19 Feb 2026 10:41:40 +0100 Subject: [PATCH 09/45] update docs --- AGENTS.md | 1 + task/Migrate_to_Effect_v4.md | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 7e90406a7..cf4e809d7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,6 +14,7 @@ This is the Effect App library repository, focusing on functional programming pa - **Conciseness**: Keep code and any wording concise and to the point. Sacrifice grammar for the sake of concision. - **Reduce comments**: Avoid comments unless absolutely required to explain unusual or complex logic. Comments in jsdocs are acceptable. - **Never import local `/repos` files**: Always use the latest online versions of packages instead. +- **Never webfetch from the `effect` and `effect-smol` repos**: just use the locally included under `repos` ### Mandatory Validation Steps diff --git a/task/Migrate_to_Effect_v4.md b/task/Migrate_to_Effect_v4.md index a7738e61b..4590460fb 100644 --- a/task/Migrate_to_Effect_v4.md +++ b/task/Migrate_to_Effect_v4.md @@ -8,7 +8,7 @@ There are migration guides: - [v3 to v4 general](/repos/effect-smol/MIGRATION.md) - [Schema v3 to v4](/repos/effect-smol/packages/effect/SCHEMA.md#migration-from-v3) -## Approach +## Steps 1. Convert `cli` - as it's a standalone utility using minimal effect libraries. 2. Convert `effect-app` core @@ -18,10 +18,12 @@ There are migration guides: Each step will be completed individually, and only move on to the next step when the current is done succesfully. For each step we should find out if we can convert 1:1 or certain things are missing preventing that. -Create task files for each, and track progress and findings in each. + +## Rules - Always check `AGENTS.md` in the root of each repository to understand rules. - You're allowed to use different versions of workspace packages within a project for the duration of the migration. +- Create task files for each Step in markdown files under `task/Migration` directory, and track progress and findings in each. ## Conversion From 02281db289dbd0912d3bc6c5e7d4549910a80878 Mon Sep 17 00:00:00 2001 From: Patrick Roza Date: Thu, 19 Feb 2026 11:01:51 +0100 Subject: [PATCH 10/45] Update Effect.Service migration recommendation --- task/Migrate_to_Effect_v4.md | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/task/Migrate_to_Effect_v4.md b/task/Migrate_to_Effect_v4.md index 4590460fb..227ce49f0 100644 --- a/task/Migrate_to_Effect_v4.md +++ b/task/Migrate_to_Effect_v4.md @@ -40,7 +40,31 @@ We start with an as close as possible 1:1 conversion. ## Concerns -- `Effect.Service` has been removed, to ease migration, we may at first copy (and adjust to effect-smol) the original implementation, export it from `effect-app/core/Effect` and replace it at a later stage. +### `Effect.Service` migration to `ServiceMap.Service` + +Before: +```ts +class GHGistService extends Effect.Service()("GHGistService", { + dependencies: [RunCommandService.Default], + effect: Effect.gen(function*() { + // ... + }) +} {} +``` + +After: +```ts +class GHGistService extends ServiceMap.Service()("GHGistService", { + make: Effect.gen(function*() { + // ... + }) +}) { + static DefaultWithoutDependencies = Layer.effect(this, this.make) + static Default = this.DefaultWithoutDependencies.pipe( + Layer.provide(RunCommandService.Default) + ) +} +``` ## Context From 48264bfd8e404c2edf254a9201335d19fdcf0b93 Mon Sep 17 00:00:00 2001 From: Patrick Roza Date: Thu, 19 Feb 2026 11:04:54 +0100 Subject: [PATCH 11/45] disable tsplugins during the migration, until after. --- tsconfig.plugins.json | 54 +++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/tsconfig.plugins.json b/tsconfig.plugins.json index 5c91759c8..f789595e4 100644 --- a/tsconfig.plugins.json +++ b/tsconfig.plugins.json @@ -1,33 +1,33 @@ { "compilerOptions": { "plugins": [ - { - "name": "ts-plugin-sort-import-suggestions", - "moveUpPatterns": [ - "\\.{1,2}/", - "^(?:\\.\\./)+", - "^#", - "^@/", - "effect", - "^@effect/" - ], - "moveDownPatterns": [ - "^node_modules/" - ], - "overrides": { - "effect-app": [] - } - }, - { - "name": "@effect/language-service", - "ignoreEffectWarningsInTscExitCode": true, - "diagnosticSeverity": { - "missingEffectServiceDependency": "error", - "effectFnOpportunity": "warning", - "globalErrorInEffectFailure": "warning", - "preferSchemaOverJson": "warning" - } - } + // { + // "name": "ts-plugin-sort-import-suggestions", + // "moveUpPatterns": [ + // "\\.{1,2}/", + // "^(?:\\.\\./)+", + // "^#", + // "^@/", + // "effect", + // "^@effect/" + // ], + // "moveDownPatterns": [ + // "^node_modules/" + // ], + // "overrides": { + // "effect-app": [] + // } + // }, + // { + // "name": "@effect/language-service", + // "ignoreEffectWarningsInTscExitCode": true, + // "diagnosticSeverity": { + // "missingEffectServiceDependency": "error", + // "effectFnOpportunity": "warning", + // "globalErrorInEffectFailure": "warning", + // "preferSchemaOverJson": "warning" + // } + // } ] } } \ No newline at end of file From e4891dc698b245683a0927c549f9e24b05afcd9f Mon Sep 17 00:00:00 2001 From: Patrick Roza Date: Thu, 19 Feb 2026 12:26:59 +0100 Subject: [PATCH 12/45] update doc --- task/Migrate_to_Effect_v4.md | 1 + 1 file changed, 1 insertion(+) diff --git a/task/Migrate_to_Effect_v4.md b/task/Migrate_to_Effect_v4.md index 227ce49f0..366b7ee7d 100644 --- a/task/Migrate_to_Effect_v4.md +++ b/task/Migrate_to_Effect_v4.md @@ -24,6 +24,7 @@ For each step we should find out if we can convert 1:1 or certain things are mis - Always check `AGENTS.md` in the root of each repository to understand rules. - You're allowed to use different versions of workspace packages within a project for the duration of the migration. - Create task files for each Step in markdown files under `task/Migration` directory, and track progress and findings in each. +- Save all conversion findings in a `task/findings.md` file to speed up future migrations. ## Conversion From 05cdc078ac16b7a5899f299ba537f474dc0e5059 Mon Sep 17 00:00:00 2001 From: Patrick Roza Date: Thu, 19 Feb 2026 12:27:37 +0100 Subject: [PATCH 13/45] VIBE updated, including findings and task md --- packages/cli/package.json | 4 +- packages/cli/src/extract.ts | 5 +- packages/cli/src/gist.ts | 196 +++-- packages/cli/src/index.ts | 1286 ++++++++++++++++---------------- packages/cli/src/os-command.ts | 41 +- pnpm-lock.yaml | 225 +++++- task/Migration/01-cli.md | 61 ++ task/findings.md | 104 +++ 8 files changed, 1110 insertions(+), 812 deletions(-) create mode 100644 task/Migration/01-cli.md create mode 100644 task/findings.md diff --git a/packages/cli/package.json b/packages/cli/package.json index 9069a6bb9..79020ad21 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -9,8 +9,8 @@ "effect-app-cli": "./bin.js" }, "dependencies": { - "@effect/cli": "^0.73.0", - "@effect/platform-node": "^0.104.0", + "@effect/platform-node": "^4.0.0-beta.5", + "effect": "^4.0.0-beta.5", "js-yaml": "4.1.1", "node-watch": "^0.7.4" }, diff --git a/packages/cli/src/extract.ts b/packages/cli/src/extract.ts index d4c2d1148..751a9cda3 100644 --- a/packages/cli/src/extract.ts +++ b/packages/cli/src/extract.ts @@ -1,5 +1,4 @@ -import { type Error as PlatformError, FileSystem, Path } from "@effect/platform" -import { Array as EffectArray, Effect, Order, pipe } from "effect" +import { Array as EffectArray, Effect, FileSystem, Order, Path, pipe, type PlatformError } from "effect" /** * Generates package.json exports mappings for TypeScript modules @@ -61,7 +60,7 @@ export const ExtractExportMappingsService = Effect.fn("effa-cli.extractExportMap const sortedMappings = pipe( exportMappings, - EffectArray.sort(Order.string), + EffectArray.sort(Order.String), EffectArray.join(",\n") ) diff --git a/packages/cli/src/gist.ts b/packages/cli/src/gist.ts index 36d485a54..abe31cffa 100644 --- a/packages/cli/src/gist.ts +++ b/packages/cli/src/gist.ts @@ -1,9 +1,7 @@ /* eslint-disable no-constant-binary-expression */ /* eslint-disable no-empty-pattern */ // import necessary modules from the libraries -import { FileSystem, Path } from "@effect/platform" - -import { Array, Config, Data, Effect, Option, ParseResult, pipe, Redacted, Schema, SynchronizedRef } from "effect" +import { Array, Config, Data, Effect, FileSystem, Layer, Option, Path, pipe, Redacted, Schema, SchemaIssue, SchemaTransformation, ServiceMap, SynchronizedRef } from "effect" import * as yaml from "js-yaml" import path from "path" @@ -44,71 +42,63 @@ export class GistEntry extends Schema.Class("GistEntry")({ * @see {@link https://docs.github.com/articles/creating-gists | GitHub Gist Documentation} * @see {@link https://github.com/orgs/community/discussions/29584 | Community Discussion on Gist Folder Support} */ -export class GistEntryDecoded extends GistEntry.transformOrFail("GistEntryDecoded")({ - files_with_name: Schema.Array(Schema.Struct({ - path: Schema.String, - name: Schema.String - })) -}, { - decode: Effect.fnUntraced(function*(entry, _, ast) { - const files_with_name = entry.files.map((file) => ({ - path: file, - name: path.basename(file) // <-- I'm using Node's path module here so that this schema works without requirements on Effect's Path module - })) - - // check for duplicate file names - const nameMap = new Map() - for (const { name, path: filePath } of files_with_name) { - if (!nameMap.has(name)) { - nameMap.set(name, []) - } - nameMap.get(name)!.push(filePath) - } +export const GistEntryDecoded = GistEntry.pipe( + Schema.decodeTo( + Schema.Struct({ + description: Schema.String, + public: Schema.Boolean, + company: Schema.String, + files: Schema.Array(Schema.String), + files_with_name: Schema.Array(Schema.Struct({ + path: Schema.String, + name: Schema.String + })) + }), + SchemaTransformation.transformOrFail({ + decode: Effect.fnUntraced(function*(entry) { + const files_with_name = entry.files.map((file) => ({ + path: file, + name: path.basename(file) // <-- I'm using Node's path module here so that this schema works without requirements on Effect's Path module + })) + + // check for duplicate file names + const nameMap = new Map() + for (const { name, path: filePath } of files_with_name) { + if (!nameMap.has(name)) { + nameMap.set(name, []) + } + nameMap.get(name)!.push(filePath) + } - // find duplicates and collect all collisions - const collisions: ParseResult.ParseIssue[] = [] - for (const [fileName, paths] of nameMap.entries()) { - if (paths.length > 1) { - collisions.push( - new ParseResult.Type( - ast, - paths, - `Duplicate file name detected: "${fileName}". Colliding paths: ${paths.join(", ")}` - ) - ) - } - } + // find duplicates and collect all collision messages + const messages: string[] = [] + for (const [fileName, paths] of nameMap.entries()) { + if (paths.length > 1) { + messages.push( + `Duplicate file name detected: "${fileName}". Colliding paths: ${paths.join(", ")}` + ) + } + } - // if there are any collisions, fail with all of them - if (Array.isNonEmptyArray(collisions)) { - return yield* Effect.fail( - new ParseResult.Composite( - ast, - entry.files, - collisions - ) - ) - } + // if there are any collisions, fail with a combined message + if (messages.length > 0) { + return yield* Effect.fail( + new SchemaIssue.InvalidValue(Option.some(entry.files), { message: messages.join("; ") }) + ) + } - return yield* Effect.succeed({ - ...entry, - files_with_name + return yield* Effect.succeed({ ...entry, files_with_name }) + }), + encode: ({ files_with_name: _, ...entry }) => Effect.succeed(entry) }) - }), - encode: (({ files_with_name, ...entry }) => ParseResult.succeed(entry)) -}) {} + ) +) +export interface GistEntryDecoded extends Schema.Schema.Type {} export class GistYAML extends Schema.Class("GistYAML")({ - gists: Schema - .Record({ - key: Schema.String, - value: GistEntryDecoded - }) - .pipe(Schema.optionalWith({ - default: () => ({}), - nullable: true, - exact: true - })), + gists: Schema.optional(Schema.NullOr( + Schema.Record(Schema.String, GistEntryDecoded) + )), settings: Schema.Struct({ token_env: Schema.String, base_directory: Schema.String @@ -176,16 +166,16 @@ class GistYAMLError extends Data.TaggedError("GistYAMLError")<{ // Services // -class GHGistService extends Effect.Service()("GHGistService", { - dependencies: [RunCommandService.Default], - effect: Effect.gen(function*() { +class GHGistService extends ServiceMap.Service()("GHGistService", { + make: Effect.gen(function*() { const CACHE_GIST_DESCRIPTION = "GIST_CACHE_DO_NOT_EDIT_effa_cli_internal" const { runGetExitCode, runGetString } = yield* RunCommandService // the client cannot recover from PlatformErrors, so we convert failures into defects to clean up the signatures const runGetExitCodeSuppressed = (...args: Parameters) => { return runGetExitCode(...args).pipe( - Effect.catchAll((e) => Effect.dieMessage(`Command failed: ${args.join(" ")}\nError: ${e.message}`)), + Effect.mapError((e) => new Error(`Command failed: ${args.join(" ")}\nError: ${e.message}`)), + Effect.orDie, Effect.asVoid ) } @@ -193,7 +183,8 @@ class GHGistService extends Effect.Service()("GHGistService", { // the client cannot recover from PlatformErrors, so we convert failures into defects to clean up the signatures const runGetStringSuppressed = (...args: Parameters) => { return runGetString(...args).pipe( - Effect.catchAll((e) => Effect.dieMessage(`Command failed: ${args.join(" ")}\nError: ${e.message}`)) + Effect.mapError((e) => new Error(`Command failed: ${args.join(" ")}\nError: ${e.message}`)), + Effect.orDie ) } @@ -218,7 +209,6 @@ class GHGistService extends Effect.Service()("GHGistService", { ) { // search for existing cache gist const output = yield* runGetStringSuppressed(`gh gist list --filter "${CACHE_GIST_DESCRIPTION}"`) - .pipe(Effect.orElse(() => Effect.succeed(""))) const lines = output.trim().split("\n").filter((line: string) => line.trim()) @@ -233,7 +223,7 @@ class GHGistService extends Effect.Service()("GHGistService", { if (!gist_id) { if (recCache) { - return yield* Effect.dieMessage("Failed to create or locate cache gist after creation attempt") + return yield* Effect.die(new Error("Failed to create or locate cache gist after creation attempt")) } return yield* new GistCacheNotFound({ message: "No gist ID found in output" }) } else { @@ -252,8 +242,10 @@ class GHGistService extends Effect.Service()("GHGistService", { if (!filesInCache.includes(`${company}.json`)) { if (recCacheCompany) { - return yield* Effect.dieMessage( - `Failed to create or locate cache entry for company ${company} after creation attempt` + return yield* Effect.die( + new Error( + `Failed to create or locate cache entry for company ${company} after creation attempt` + ) ) } return yield* new GistCacheOfCompanyNotFound({ @@ -265,7 +257,7 @@ class GHGistService extends Effect.Service()("GHGistService", { const entries = yield* pipe( cacheContent, - pipe(Schema.parseJson(GistCacheEntries), Schema.decodeUnknown), + Schema.decodeUnknownEffect(Schema.fromJsonString(GistCacheEntries)), Effect.orDie ) @@ -310,7 +302,7 @@ class GHGistService extends Effect.Service()("GHGistService", { function*(cache: GistCache) { const cacheJson = yield* pipe( cache.entries, - pipe(Schema.parseJson(GistCacheEntries), Schema.encodeUnknown), + Schema.encodeUnknownEffect(Schema.fromJsonString(GistCacheEntries)), // cannot recover from parse errors in any case, better to die here instead of cluttering the signature Effect.orDie ) @@ -353,7 +345,7 @@ class GHGistService extends Effect.Service()("GHGistService", { gistUrl, extractGistIdFromUrl, Option.match({ - onNone: () => Effect.dieMessage(`Failed to extract gist ID from URL: ${gistUrl}`), + onNone: () => Effect.die(new Error(`Failed to extract gist ID from URL: ${gistUrl}`)), onSome: (id) => Effect .succeed( @@ -396,20 +388,16 @@ class GHGistService extends Effect.Service()("GHGistService", { // filter file names by environment prefix and remove the prefix // files in gists are prefixed with "env." to support multiple environments - return Array.filterMap( - output - .trim() - .split("\n"), - (fn) => { + return output + .trim() + .split("\n") + .flatMap((fn) => { const fnTrimmed = fn.trim() if (!fnTrimmed.startsWith(env + ".")) { - return Option.none() + return [] } - return Option.some( - fnTrimmed.substring(env.length + 1) // remove env prefix and dot - ) - } - ) + return [fnTrimmed.substring(env.length + 1)] // remove env prefix and dot + }) } ) @@ -500,8 +488,10 @@ class GHGistService extends Effect.Service()("GHGistService", { const login = Effect.fn("GHGistService.login")(function*(token: string) { if ((yield* runGetExitCode("gh --version").pipe(Effect.orDie)) !== 0) { - return yield* Effect.dieMessage( - "GitHub CLI (gh) is not installed or not found in PATH. Please install it to use the gist command." + return yield* Effect.die( + new Error( + "GitHub CLI (gh) is not installed or not found in PATH. Please install it to use the gist command." + ) ) } @@ -608,13 +598,15 @@ class GHGistService extends Effect.Service()("GHGistService", { deleteGist } }) -}) {} +}) { + static DefaultWithoutDependencies = Layer.effect(this, this.make) + static Default = this.DefaultWithoutDependencies.pipe( + Layer.provide(RunCommandService.Default) + ) +} -// @effect-diagnostics-next-line missingEffectServiceDependency:off -export class GistHandler extends Effect.Service()("GistHandler", { - accessors: true, - dependencies: [GHGistService.Default], - effect: Effect.gen(function*() { +export class GistHandler extends ServiceMap.Service()("GistHandler", { + make: Effect.gen(function*() { const GH = yield* GHGistService // I prefer to provide these two only once during the main CLI pipeline setup @@ -624,10 +616,9 @@ export class GistHandler extends Effect.Service()("GistHandler", { return { handler: Effect.fn("effa-cli.gist.GistHandler")(function*({ YAMLPath }: { YAMLPath: string }) { // load company and environment from environment variables - const CONFIG = yield* Effect.all({ - company: Config.string("COMPANY"), - env: Config.string("ENV").pipe(Config.withDefault("local-dev")) - }) + const company = yield* Config.string("COMPANY") + const env = yield* Config.string("ENV").pipe(Config.withDefault(() => "local-dev")) + const CONFIG = { company, env } yield* Effect.logInfo(`Company: ${CONFIG.company}, ENV: ${CONFIG.env}`) @@ -649,7 +640,7 @@ export class GistHandler extends Effect.Service()("GistHandler", { } }) ), - Effect.andThen(Schema.decodeUnknown(GistYAML)) + Effect.andThen(Schema.decodeUnknownEffect(GistYAML)) ) // load GitHub token securely from environment variable @@ -665,7 +656,7 @@ export class GistHandler extends Effect.Service()("GistHandler", { // filter YAML gists by company to ensure isolation between different organizations // this prevents cross-company gist operations and maintains data separation const thisCompanyGistsFromYaml = Object - .entries(configFromYaml.gists) + .entries(configFromYaml.gists ?? {}) .filter(([, v]) => v.company === CONFIG.company) for ( @@ -815,4 +806,9 @@ export class GistHandler extends Effect.Service()("GistHandler", { }) } }) -}) {} +}) { + static DefaultWithoutDependencies = Layer.effect(this, this.make) + static Default = this.DefaultWithoutDependencies.pipe( + Layer.provide(GHGistService.Default) + ) +} diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 3cf3cc079..7328335cf 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -1,738 +1,724 @@ /* eslint-disable no-constant-binary-expression */ /* eslint-disable no-empty-pattern */ // import necessary modules from the libraries -import { Args, Command, Options, Prompt } from "@effect/cli" -import { FileSystem, Path } from "@effect/platform" -import { NodeContext, NodeRuntime } from "@effect/platform-node" +import { NodeRuntime, NodeServices } from "@effect/platform-node" +import { Argument, Command, Flag, Prompt } from "effect/unstable/cli" -import { type CommandExecutor } from "@effect/platform/CommandExecutor" -import { type PlatformError } from "@effect/platform/Error" -import { Effect, Layer, Option, Stream, type Types } from "effect" +import { Effect, FileSystem, Layer, Option, Path, Stream } from "effect" import { ExtractExportMappingsService } from "./extract.js" import { GistHandler } from "./gist.js" import { RunCommandService } from "./os-command.js" import { packages } from "./shared.js" -Effect - .fn("effa-cli")(function*() { - const fs = yield* FileSystem.FileSystem - const path = yield* Path.Path - const extractExportMappings = yield* ExtractExportMappingsService - const { runGetExitCode } = yield* RunCommandService - - yield* Effect.addFinalizer(() => Effect.logInfo(`CLI has finished executing`)) - - /** - * Updates effect-app packages to their latest versions using npm-check-updates. - * Runs both at workspace root and recursively in all workspace packages. - */ - const updateEffectAppPackages = Effect.fn("effa-cli.ue.updateEffectAppPackages")(function*() { - const filters = ["effect-app", "@effect-app/*"] - for (const filter of filters) { - yield* runGetExitCode(`pnpm exec ncu -u --filter "${filter}"`) - yield* runGetExitCode(`pnpm -r exec ncu -u --filter "${filter}"`) - } - })() - - /** - * Updates Effect ecosystem packages to their latest versions using npm-check-updates. - * Covers core Effect packages, Effect ecosystem packages, and Effect Atom packages. - * Runs both at workspace root and recursively in all workspace packages. - */ - const updateEffectPackages = Effect.fn("effa-cli.ue.updateEffectPackages")(function*() { - const effectFilters = ["effect", "@effect/*", "@effect-atom/*"] - for (const filter of effectFilters) { - yield* runGetExitCode(`pnpm exec ncu -u --filter "${filter}"`) - yield* runGetExitCode(`pnpm -r exec ncu -u --filter "${filter}"`) - } - })() +NodeRuntime.runMain( + Effect + .fn("effa-cli")(function*() { + const fs = yield* FileSystem.FileSystem + const path = yield* Path.Path + const extractExportMappings = yield* ExtractExportMappingsService + const { runGetExitCode } = yield* RunCommandService + + yield* Effect.addFinalizer(() => Effect.logInfo(`CLI has finished executing`)) + + /** + * Updates effect-app packages to their latest versions using npm-check-updates. + * Runs both at workspace root and recursively in all workspace packages. + */ + const updateEffectAppPackages = Effect.fn("effa-cli.ue.updateEffectAppPackages")(function*() { + const filters = ["effect-app", "@effect-app/*"] + for (const filter of filters) { + yield* runGetExitCode(`pnpm exec ncu -u --filter "${filter}"`) + yield* runGetExitCode(`pnpm -r exec ncu -u --filter "${filter}"`) + } + })() + + /** + * Updates Effect ecosystem packages to their latest versions using npm-check-updates. + * Covers core Effect packages, Effect ecosystem packages, and Effect Atom packages. + * Runs both at workspace root and recursively in all workspace packages. + */ + const updateEffectPackages = Effect.fn("effa-cli.ue.updateEffectPackages")(function*() { + const effectFilters = ["effect", "@effect/*", "@effect-atom/*"] + for (const filter of effectFilters) { + yield* runGetExitCode(`pnpm exec ncu -u --filter "${filter}"`) + yield* runGetExitCode(`pnpm -r exec ncu -u --filter "${filter}"`) + } + })() + + /** + * Updates all packages except Effect and Effect-App ecosystem packages to their latest versions using npm-check-updates. + * Excludes core Effect packages, Effect ecosystem packages, Effect Atom packages, and Effect-App packages. + * Preserves existing rejections from .ncurc.json configuration. + * Runs both at workspace root and recursively in all workspace packages. + */ + const updatePackages = Effect.fn("effa-cli.update-packages.updatePackages")(function*() { + const effectFilters = ["effect", "@effect/*", "@effect-atom/*", "effect-app", "@effect-app/*"] + + // read existing .ncurc.json to preserve existing reject patterns + let existingRejects: string[] = [] + const ncurcPath = "./.ncurc.json" + + if (yield* fs.exists(ncurcPath)) { + const ncurcContent = yield* fs.readFileString(ncurcPath) + const ncurc = JSON.parse(ncurcContent) + if (ncurc.reject && Array.isArray(ncurc.reject)) { + existingRejects = ncurc.reject + } + } - /** - * Updates all packages except Effect and Effect-App ecosystem packages to their latest versions using npm-check-updates. - * Excludes core Effect packages, Effect ecosystem packages, Effect Atom packages, and Effect-App packages. - * Preserves existing rejections from .ncurc.json configuration. - * Runs both at workspace root and recursively in all workspace packages. - */ - const updatePackages = Effect.fn("effa-cli.update-packages.updatePackages")(function*() { - const effectFilters = ["effect", "@effect/*", "@effect-atom/*", "effect-app", "@effect-app/*"] - - // read existing .ncurc.json to preserve existing reject patterns - let existingRejects: string[] = [] - const ncurcPath = "./.ncurc.json" - - if (yield* fs.exists(ncurcPath)) { - const ncurcContent = yield* fs.readFileString(ncurcPath) - const ncurc = JSON.parse(ncurcContent) - if (ncurc.reject && Array.isArray(ncurc.reject)) { - existingRejects = ncurc.reject + const allRejects = [...existingRejects, ...effectFilters] + yield* Effect.logInfo(`Excluding packages from update: ${allRejects.join(", ")}`) + const rejectArgs = allRejects.map((filter) => `--reject "${filter}"`).join(" ") + + yield* runGetExitCode(`pnpm exec ncu -u ${rejectArgs}`) + yield* runGetExitCode(`pnpm -r exec ncu -u ${rejectArgs}`) + })() + + /** + * Links local effect-app packages by adding file resolutions to package.json. + * Updates the package.json with file: protocol paths pointing to the local effect-app-libs directory, + * then runs pnpm install to apply the changes. + * + * @param effectAppLibsPath - Path to the local effect-app-libs directory + * @returns An Effect that succeeds when linking is complete + */ + const linkPackages = Effect.fnUntraced(function*(effectAppLibsPath: string) { + yield* Effect.logInfo("Linking local effect-app packages...") + + const packageJsonPath = "./package.json" + const packageJsonContent = yield* fs.readFileString(packageJsonPath) + const pj = JSON.parse(packageJsonContent) + + const resolutions = { + ...pj.resolutions, + "@effect-app/eslint-codegen-model": "file:" + effectAppLibsPath + "/packages/eslint-codegen-model", + "effect-app": "file:" + effectAppLibsPath + "/packages/effect-app", + "@effect-app/infra": "file:" + effectAppLibsPath + "/packages/infra", + "@effect-app/vue": "file:" + effectAppLibsPath + "/packages/vue", + "@effect-app/vue-components": "file:" + effectAppLibsPath + "/packages/vue-components", + "@effect-app/eslint-shared-config": "file:" + effectAppLibsPath + "/packages/eslint-shared-config", + ...packages.reduce((acc, p) => ({ ...acc, [p]: `file:${effectAppLibsPath}/node_modules/${p}` }), {}) } - } - const allRejects = [...existingRejects, ...effectFilters] - yield* Effect.logInfo(`Excluding packages from update: ${allRejects.join(", ")}`) - const rejectArgs = allRejects.map((filter) => `--reject "${filter}"`).join(" ") + pj.resolutions = resolutions - yield* runGetExitCode(`pnpm exec ncu -u ${rejectArgs}`) - yield* runGetExitCode(`pnpm -r exec ncu -u ${rejectArgs}`) - })() + yield* fs.writeFileString(packageJsonPath, JSON.stringify(pj, null, 2)) + yield* Effect.logInfo("Updated package.json with local file resolutions") - /** - * Links local effect-app packages by adding file resolutions to package.json. - * Updates the package.json with file: protocol paths pointing to the local effect-app-libs directory, - * then runs pnpm install to apply the changes. - * - * @param effectAppLibsPath - Path to the local effect-app-libs directory - * @returns An Effect that succeeds when linking is complete - */ - const linkPackages = Effect.fnUntraced(function*(effectAppLibsPath: string) { - yield* Effect.logInfo("Linking local effect-app packages...") - - const packageJsonPath = "./package.json" - const packageJsonContent = yield* fs.readFileString(packageJsonPath) - const pj = JSON.parse(packageJsonContent) - - const resolutions = { - ...pj.resolutions, - "@effect-app/eslint-codegen-model": "file:" + effectAppLibsPath + "/packages/eslint-codegen-model", - "effect-app": "file:" + effectAppLibsPath + "/packages/effect-app", - "@effect-app/infra": "file:" + effectAppLibsPath + "/packages/infra", - "@effect-app/vue": "file:" + effectAppLibsPath + "/packages/vue", - "@effect-app/vue-components": "file:" + effectAppLibsPath + "/packages/vue-components", - "@effect-app/eslint-shared-config": "file:" + effectAppLibsPath + "/packages/eslint-shared-config", - ...packages.reduce((acc, p) => ({ ...acc, [p]: `file:${effectAppLibsPath}/node_modules/${p}` }), {}) - } - - pj.resolutions = resolutions - - yield* fs.writeFileString(packageJsonPath, JSON.stringify(pj, null, 2)) - yield* Effect.logInfo("Updated package.json with local file resolutions") - - yield* runGetExitCode("pnpm i") - - yield* Effect.logInfo("Successfully linked local packages") - }) - - /** - * Unlinks local effect-app packages by removing file resolutions from package.json. - * Filters out all effect-app related file: protocol resolutions from package.json, - * then runs pnpm install to restore registry packages. - * - * @returns An Effect that succeeds when unlinking is complete - */ - const unlinkPackages = Effect.fnUntraced(function*() { - yield* Effect.logInfo("Unlinking local effect-app packages...") - - const packageJsonPath = "./package.json" - const packageJsonContent = yield* fs.readFileString(packageJsonPath) - const pj = JSON.parse(packageJsonContent) - - const filteredResolutions = Object.entries(pj.resolutions as Record).reduce( - (acc, [k, v]) => { - if (k.startsWith("@effect-app/") || k === "effect-app" || packages.includes(k)) return acc - acc[k] = v - return acc - }, - {} as Record - ) + yield* runGetExitCode("pnpm i") - pj.resolutions = filteredResolutions + yield* Effect.logInfo("Successfully linked local packages") + }) - yield* fs.writeFileString(packageJsonPath, JSON.stringify(pj, null, 2)) - yield* Effect.logInfo("Removed effect-app file resolutions from package.json") + /** + * Unlinks local effect-app packages by removing file resolutions from package.json. + * Filters out all effect-app related file: protocol resolutions from package.json, + * then runs pnpm install to restore registry packages. + * + * @returns An Effect that succeeds when unlinking is complete + */ + const unlinkPackages = Effect.fnUntraced(function*() { + yield* Effect.logInfo("Unlinking local effect-app packages...") + + const packageJsonPath = "./package.json" + const packageJsonContent = yield* fs.readFileString(packageJsonPath) + const pj = JSON.parse(packageJsonContent) + + const filteredResolutions = Object.entries(pj.resolutions as Record).reduce( + (acc, [k, v]) => { + if (k.startsWith("@effect-app/") || k === "effect-app" || packages.includes(k)) return acc + acc[k] = v + return acc + }, + {} as Record + ) - yield* runGetExitCode("pnpm i") - yield* Effect.logInfo("Successfully unlinked local packages") - })() + pj.resolutions = filteredResolutions + + yield* fs.writeFileString(packageJsonPath, JSON.stringify(pj, null, 2)) + yield* Effect.logInfo("Removed effect-app file resolutions from package.json") + + yield* runGetExitCode("pnpm i") + yield* Effect.logInfo("Successfully unlinked local packages") + })() + + /** + * Monitors controller files for changes and runs eslint on related controllers.ts/routes.ts files. + * Watches for .controllers. files and triggers eslint fixes on parent directory's controller files. + * + * @param watchPath - The path to watch for controller changes + * @param debug - Whether to enable debug logging + * @returns An Effect that sets up controller file monitoring + */ + const monitorChildIndexes = Effect.fn("effa-cli.index-multi.monitorChildIndexes")( + function*(watchPath: string) { + yield* Effect.logInfo(`Starting controller monitoring for: ${watchPath}`) + + const watchStream = fs.watch(watchPath) + + yield* watchStream + .pipe( + Stream.runForEach( + Effect.fn("effa-cli.monitorChildIndexes.handleEvent")(function*(event) { + const pathParts = event.path.split("/") + const fileName = pathParts[pathParts.length - 1] + const isController = fileName?.toLowerCase().includes(".controllers.") + + if (!isController) return + + let i = 1 + const reversedParts = pathParts.toReversed() + + while (i < reversedParts.length) { + const candidateFiles = ["controllers.ts", "routes.ts"] + .map((f) => [...pathParts.slice(0, pathParts.length - i), f].join("/")) + + const existingFiles: string[] = [] + for (const file of candidateFiles) { + const exists = yield* fs.exists(file) + if (exists) existingFiles.push(file) + } + + if (existingFiles.length > 0) { + yield* Effect.logInfo( + `Controller change detected: ${event.path}, fixing files: ${existingFiles.join(", ")}` + ) + + const eslintArgs = existingFiles.map((f) => `"../${f}"`).join(" ") + yield* runGetExitCode(`cd api && pnpm eslint --fix ${eslintArgs}`) + break + } + i++ + } + }) + ), + Effect.andThen( + Effect.addFinalizer(() => Effect.logInfo(`Stopped monitoring child indexes in: ${watchPath}`)) + ), + Effect.forkScoped + ) + } + ) - /** - * Monitors controller files for changes and runs eslint on related controllers.ts/routes.ts files. - * Watches for .controllers. files and triggers eslint fixes on parent directory's controller files. - * - * @param watchPath - The path to watch for controller changes - * @param debug - Whether to enable debug logging - * @returns An Effect that sets up controller file monitoring - */ - const monitorChildIndexes = Effect.fn("effa-cli.index-multi.monitorChildIndexes")( - function*(watchPath: string) { - yield* Effect.logInfo(`Starting controller monitoring for: ${watchPath}`) - - const watchStream = fs.watch(watchPath, { recursive: true }) - - yield* watchStream - .pipe( - Stream.runForEach( - Effect.fn("effa-cli.monitorChildIndexes.handleEvent")(function*(event) { - const pathParts = event.path.split("/") - const fileName = pathParts[pathParts.length - 1] - const isController = fileName?.toLowerCase().includes(".controllers.") + /** + * Monitors a directory for changes and runs eslint on the specified index file. + * Triggers eslint fixes when any file in the directory changes (except the index file itself). + * + * @param watchPath - The path to watch for changes + * @param indexFile - The index file to run eslint on when changes occur + * @param debug - Whether to enable debug logging + * @returns An Effect that sets up root index monitoring + */ + const monitorRootIndexes = Effect.fn("effa-cli.index-multi.monitorRootIndexes")( + function*(watchPath: string, indexFile: string) { + yield* Effect.logInfo(`Starting root index monitoring for: ${watchPath} -> ${indexFile}`) + + const watchStream = fs.watch(watchPath) + + yield* watchStream + .pipe( + Stream.runForEach( + Effect.fn("effa-cli.index-multi.monitorRootIndexes.handleEvent")(function*(event) { + if (event.path.endsWith(indexFile)) return + + yield* Effect.logInfo(`Root change detected: ${event.path}, fixing: ${indexFile}`) + + yield* runGetExitCode(`pnpm eslint --fix "${indexFile}"`) + }) + ), + Effect.andThen( + Effect.addFinalizer(() => + Effect.logInfo(`Stopped monitoring root indexes in: ${watchPath} -> ${indexFile}`) + ) + ), + Effect.forkScoped + ) + } + ) - if (!isController) return + /** + * Sets up comprehensive index monitoring for a given path. + * Combines both child controller monitoring and root index monitoring. + * + * @param watchPath - The path to monitor + * @param debug - Whether to enable debug logging + * @returns An Effect that sets up all index monitoring for the path + */ + const monitorIndexes = Effect.fn("effa-cli.index-multi.monitorIndexes")( + function*(watchPath: string) { + yield* Effect.logInfo(`Setting up index monitoring for path: ${watchPath}`) - let i = 1 - const reversedParts = pathParts.toReversed() + const indexFile = watchPath + "/index.ts" - while (i < reversedParts.length) { - const candidateFiles = ["controllers.ts", "routes.ts"] - .map((f) => [...pathParts.slice(0, pathParts.length - i), f].join("/")) + const monitors = [monitorChildIndexes(watchPath)] - const existingFiles: string[] = [] - for (const file of candidateFiles) { - const exists = yield* fs.exists(file) - if (exists) existingFiles.push(file) - } + if (yield* fs.exists(indexFile)) { + monitors.push(monitorRootIndexes(watchPath, indexFile)) + } else { + yield* Effect.logWarning(`Index file ${indexFile} does not exist`) + } - if (existingFiles.length > 0) { - yield* Effect.logInfo( - `Controller change detected: ${event.path}, fixing files: ${existingFiles.join(", ")}` - ) + yield* Effect.logInfo(`Starting ${monitors.length} monitor(s) for ${watchPath}`) - const eslintArgs = existingFiles.map((f) => `"../${f}"`).join(" ") - yield* runGetExitCode(`cd api && pnpm eslint --fix ${eslintArgs}`) - break - } - i++ + yield* Effect.all(monitors, { concurrency: monitors.length }) + } + ) + + /** + * Updates a package.json file with generated exports mappings for TypeScript modules. + * Scans TypeScript source files and creates export entries that map module paths + * to their compiled JavaScript and TypeScript declaration files. + * + * @param startDir - The starting directory path for resolving relative paths + * @param p - The package directory path to process + * @param levels - Optional depth limit for export filtering (0 = no limit) + * @returns An Effect that succeeds when the package.json is updated + */ + const packagejsonUpdater = Effect.fn("effa-cli.packagejsonUpdater")( + function*(startDir: string, p: string, levels = 0) { + yield* Effect.logInfo(`Generating exports for ${p}`) + + const exportMappings = yield* extractExportMappings(path.resolve(startDir, p)) + + // if exportMappings is empty skip export generation + if (exportMappings === "") { + yield* Effect.logInfo(`No src directory found for ${p}, skipping export generation`) + return + } + + const sortedExportEntries = JSON.parse( + `{ ${exportMappings} }` + ) as Record< + string, + unknown + > + + const filteredExportEntries = levels + ? Object + .keys(sortedExportEntries) + // filter exports by directory depth - only include paths up to specified levels deep + .filter((_) => _.split("/").length <= (levels + 1 /* `./` */)) + .reduce( + (prev, cur) => ({ ...prev, [cur]: sortedExportEntries[cur] }), + {} as Record + ) + : sortedExportEntries + + const packageExports = { + ...((yield* fs.exists(p + "/src/index.ts")) + && { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" } - }) - ), - Effect.andThen( - Effect.addFinalizer(() => Effect.logInfo(`Stopped monitoring child indexes in: ${watchPath}`)) - ), - Effect.forkScoped + }), + ...Object + .keys(filteredExportEntries) + .reduce( + (prev, cur) => ({ + ...prev, + // exclude index files and internal modules from package exports: + // - skip "./index" to avoid conflicts with the main "." export + // - skip "/internal/" paths to keep internal modules private + ...cur !== "./index" && !cur.includes("/internal/") && { [cur]: filteredExportEntries[cur] } + }), + {} as Record + ) + } + + const pkgJson = JSON.parse(yield* fs.readFileString(p + "/package.json", "utf-8")) + + pkgJson.exports = packageExports + + yield* Effect.logInfo(`Writing updated package.json for ${p}`) + + return yield* fs.writeFileString( + p + "/package.json", + JSON.stringify(pkgJson, null, 2) ) - } - ) + } + ) - /** - * Monitors a directory for changes and runs eslint on the specified index file. - * Triggers eslint fixes when any file in the directory changes (except the index file itself). - * - * @param watchPath - The path to watch for changes - * @param indexFile - The index file to run eslint on when changes occur - * @param debug - Whether to enable debug logging - * @returns An Effect that sets up root index monitoring - */ - const monitorRootIndexes = Effect.fn("effa-cli.index-multi.monitorRootIndexes")( - function*(watchPath: string, indexFile: string) { - yield* Effect.logInfo(`Starting root index monitoring for: ${watchPath} -> ${indexFile}`) - - const watchStream = fs.watch(watchPath) - - yield* watchStream - .pipe( - Stream.runForEach( - Effect.fn("effa-cli.index-multi.monitorRootIndexes.handleEvent")(function*(event) { - if (event.path.endsWith(indexFile)) return + /** + * Monitors a directory for TypeScript file changes and automatically updates package.json exports. + * Generates initial package.json exports, then watches the src directory for changes to regenerate exports. + * + * @param watchPath - The directory path containing the package.json and src to monitor + * @param levels - Optional depth limit for export filtering (0 = no limit) + * @returns An Effect that sets up package.json monitoring + */ + const monitorPackageJson = Effect.fn("effa-cli.monitorPackageJson")( + function*(startDir: string, watchPath: string, levels = 0) { + yield* packagejsonUpdater(startDir, watchPath, levels) + + const srcPath = watchPath === "." ? "./src" : `${watchPath}/src` + + if (!(yield* fs.exists(srcPath))) { + yield* Effect.logWarning(`Source directory ${srcPath} does not exist - skipping monitoring`) + return + } - yield* Effect.logInfo(`Root change detected: ${event.path}, fixing: ${indexFile}`) + const watchStream = fs.watch(srcPath) - yield* runGetExitCode(`pnpm eslint --fix "${indexFile}"`) + yield* watchStream.pipe( + Stream.runForEach( + Effect.fn("effa-cli.monitorPackageJson.handleEvent")(function*(_) { + yield* packagejsonUpdater(startDir, watchPath, levels) }) ), Effect.andThen( - Effect.addFinalizer(() => - Effect.logInfo(`Stopped monitoring root indexes in: ${watchPath} -> ${indexFile}`) - ) + Effect.addFinalizer(() => Effect.logInfo(`Stopped monitoring package.json for: ${watchPath}`)) ), Effect.forkScoped ) - } - ) - - /** - * Sets up comprehensive index monitoring for a given path. - * Combines both child controller monitoring and root index monitoring. - * - * @param watchPath - The path to monitor - * @param debug - Whether to enable debug logging - * @returns An Effect that sets up all index monitoring for the path - */ - const monitorIndexes = Effect.fn("effa-cli.index-multi.monitorIndexes")( - function*(watchPath: string) { - yield* Effect.logInfo(`Setting up index monitoring for path: ${watchPath}`) - - const indexFile = watchPath + "/index.ts" - - const monitors = [monitorChildIndexes(watchPath)] - - if (yield* fs.exists(indexFile)) { - monitors.push(monitorRootIndexes(watchPath, indexFile)) - } else { - yield* Effect.logWarning(`Index file ${indexFile} does not exist`) } + ) - yield* Effect.logInfo(`Starting ${monitors.length} monitor(s) for ${watchPath}`) - - yield* Effect.all(monitors, { concurrency: monitors.length }) - } - ) + /* + * CLI + */ - /** - * Updates a package.json file with generated exports mappings for TypeScript modules. - * Scans TypeScript source files and creates export entries that map module paths - * to their compiled JavaScript and TypeScript declaration files. - * - * @param startDir - The starting directory path for resolving relative paths - * @param p - The package directory path to process - * @param levels - Optional depth limit for export filtering (0 = no limit) - * @returns An Effect that succeeds when the package.json is updated - */ - const packagejsonUpdater = Effect.fn("effa-cli.packagejsonUpdater")( - function*(startDir: string, p: string, levels = 0) { - yield* Effect.logInfo(`Generating exports for ${p}`) - - const exportMappings = yield* extractExportMappings(path.resolve(startDir, p)) - - // if exportMappings is empty skip export generation - if (exportMappings === "") { - yield* Effect.logInfo(`No src directory found for ${p}, skipping export generation`) - return - } + const WrapAsOption = Flag.string("wrap").pipe( + Flag.withAlias("w"), + Flag.optional, + Flag.withDescription( + "Wrap child bash command: the lifetime of the CLI command will be tied to the child process" + ) + ) - const sortedExportEntries = JSON.parse( - `{ ${exportMappings} }` - ) as Record< - string, - unknown - > - - const filteredExportEntries = levels - ? Object - .keys(sortedExportEntries) - // filter exports by directory depth - only include paths up to specified levels deep - .filter((_) => _.split("/").length <= (levels + 1 /* `./` */)) - .reduce( - (prev, cur) => ({ ...prev, [cur]: sortedExportEntries[cur] }), - {} as Record - ) - : sortedExportEntries - - const packageExports = { - ...((yield* fs.exists(p + "/src/index.ts")) - && { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - } - }), - ...Object - .keys(filteredExportEntries) - .reduce( - (prev, cur) => ({ - ...prev, - // exclude index files and internal modules from package exports: - // - skip "./index" to avoid conflicts with the main "." export - // - skip "/internal/" paths to keep internal modules private - ...cur !== "./index" && !cur.includes("/internal/") && { [cur]: filteredExportEntries[cur] } - }), - {} as Record - ) - } + // has prio over WrapAsOption + const WrapAsArg = Argument + .string("wrap") + .pipe( + Argument.atLeast(1), + Argument.optional, + Argument.withDescription( + "Wrap child bash command: the lifetime of the CLI command will be tied to the child process" + ) + ) - const pkgJson = JSON.parse(yield* fs.readFileString(p + "/package.json", "utf-8")) + /** + * Creates a command that automatically includes wrap functionality for executing child bash commands. + * Combines both option-based (--wrap) and argument-based wrap parameters, giving priority to arguments. + * If a wrap command is provided, it will be executed **after** the main command handler. + * + * @param name - The command name + * @param config - The command configuration (options, args, etc.) + * @param handler - The main command handler function + * @param completionMessage - Optional message to log when the command completes + * @returns A Command with integrated wrap functionality + */ + const makeCommandWithWrap = ( + name: Name, + config: Config, + handler: (_: any) => Effect.Effect, + completionMessage?: string + ) => + Command.make( + name, + { ...config, wo: WrapAsOption, wa: WrapAsArg }, + Effect.fn("effa-cli.withWrapHandler")(function*(_) { + const { wa, wo, ...cfg } = _ as unknown as { + wo: Option.Option + wa: Option.Option> + } - pkgJson.exports = packageExports + if (completionMessage) { + yield* Effect.addFinalizer(() => Effect.logInfo(completionMessage)) + } - yield* Effect.logInfo(`Writing updated package.json for ${p}`) + const wrapOption = Option.orElse(wa, () => wo) - return yield* fs.writeFileString( - p + "/package.json", - JSON.stringify(pkgJson, null, 2) - ) - } - ) + yield* handler(cfg as any) - /** - * Monitors a directory for TypeScript file changes and automatically updates package.json exports. - * Generates initial package.json exports, then watches the src directory for changes to regenerate exports. - * - * @param watchPath - The directory path containing the package.json and src to monitor - * @param levels - Optional depth limit for export filtering (0 = no limit) - * @returns An Effect that sets up package.json monitoring - */ - const monitorPackageJson = Effect.fn("effa-cli.monitorPackageJson")( - function*(startDir: string, watchPath: string, levels = 0) { - yield* packagejsonUpdater(startDir, watchPath, levels) - - const srcPath = watchPath === "." ? "./src" : `${watchPath}/src` - - if (!(yield* fs.exists(srcPath))) { - yield* Effect.logWarning(`Source directory ${srcPath} does not exist - skipping monitoring`) - return - } + if (Option.isSome(wrapOption)) { + const val: string = Array.isArray(wrapOption.value) + ? wrapOption.value.join(" ") + : wrapOption.value as string - const watchStream = fs.watch(srcPath, { recursive: true }) + yield* Effect.logInfo(`Spawning child command: ${val}`) + const exitCode = yield* runGetExitCode(val) + if (exitCode !== 0) { + return yield* Effect.sync(() => process.exit(exitCode)) + } + } - yield* watchStream.pipe( - Stream.runForEach( - Effect.fn("effa-cli.monitorPackageJson.handleEvent")(function*(_) { - yield* packagejsonUpdater(startDir, watchPath, levels) - }) - ), - Effect.andThen( - Effect.addFinalizer(() => Effect.logInfo(`Stopped monitoring package.json for: ${watchPath}`)) - ), - Effect.forkScoped + return + }, (_) => Effect.scoped(_)) ) - } - ) - - /* - * CLI - */ - - const WrapAsOption = Options.text("wrap").pipe( - Options.withAlias("w"), - Options.optional, - Options.withDescription( - "Wrap child bash command: the lifetime of the CLI command will be tied to the child process" - ) - ) - // has prio over WrapAsOption - const WrapAsArg = Args - .text({ - name: "wrap" - }) - .pipe( - Args.atLeast(1), - Args.optional, - Args.withDescription( - "Wrap child bash command: the lifetime of the CLI command will be tied to the child process" + const EffectAppLibsPath = Argument + .directory("effect-app-libs-path", { mustExist: true }) + .pipe( + Argument.withDefault("../../effect-app/libs"), + Argument.withDescription("Path to the effect-app-libs directory") ) - ) - /** - * Creates a command that automatically includes wrap functionality for executing child bash commands. - * Combines both option-based (--wrap) and argument-based wrap parameters, giving priority to arguments. - * If a wrap command is provided, it will be executed **after** the main command handler. - * - * @param name - The command name - * @param config - The command configuration (options, args, etc.) - * @param handler - The main command handler function - * @param completionMessage - Optional message to log when the command completes - * @returns A Command with integrated wrap functionality - */ - const makeCommandWithWrap = ( - name: Name, - config: Config, - handler: (_: Types.Simplify>) => Effect.Effect, - completionMessage?: string - ): Command.Command< - Name, - CommandExecutor | R, - PlatformError | E, - Types.Simplify> - > => - Command.make( - name, - { ...config, wo: WrapAsOption, wa: WrapAsArg }, - Effect.fn("effa-cli.withWrapHandler")(function*(_) { - const { wa, wo, ...cfg } = _ as unknown as { - wo: Option.Option - wa: Option.Option<[string, ...string[]]> - } & Types.Simplify> - - if (completionMessage) { - yield* Effect.addFinalizer(() => Effect.logInfo(completionMessage)) - } + const link = Command + .make( + "link", + { effectAppLibsPath: EffectAppLibsPath }, + Effect.fn("effa-cli.link")(function*({ effectAppLibsPath }) { + return yield* linkPackages(effectAppLibsPath) + }) + ) + .pipe(Command.withDescription("Link local effect-app packages using file resolutions")) + + const unlink = Command + .make( + "unlink", + {}, + Effect.fn("effa-cli.unlink")(function*({}) { + return yield* unlinkPackages + }) + ) + .pipe(Command.withDescription("Remove effect-app file resolutions and restore npm registry packages")) + + const ue = Command + .make( + "ue", + {}, + Effect.fn("effa-cli.ue")(function*({}) { + yield* Effect.logInfo("Update effect-app and/or effect packages") + + const prompted = yield* Prompt.select({ + choices: [ + { + title: "effect-app", + description: "Update only effect-app packages", + value: "effect-app" + }, + { + title: "effect", + description: "Update only effect packages", + value: "effect" + }, + { + title: "both", + description: "Update both effect-app and effect packages", + value: "both" + } + ], + message: "Select an option" + }) - const wrapOption = Option.orElse(wa, () => wo) + switch (prompted) { + case "effect-app": + return yield* updateEffectAppPackages.pipe( + Effect.andThen(runGetExitCode("pnpm i")) + ) + + case "effect": + return yield* updateEffectPackages.pipe( + Effect.andThen(runGetExitCode("pnpm i")) + ) + case "both": + return yield* updateEffectPackages.pipe( + Effect.andThen(updateEffectAppPackages), + Effect.andThen(runGetExitCode("pnpm i")) + ) + } + }) + ) + .pipe(Command.withDescription("Update effect-app and/or effect packages")) - yield* handler(cfg as any) + const up = Command + .make( + "up", + {}, + Effect.fn("effa-cli.update-packages")(function*({}) { + yield* Effect.logInfo("Updating all packages except Effect/Effect-App ecosystem packages...") - if (Option.isSome(wrapOption)) { - const val = Array.isArray(wrapOption.value) - ? wrapOption.value.join(" ") - : wrapOption.value + return yield* updatePackages.pipe( + Effect.andThen(runGetExitCode("pnpm i")) + ) + }) + ) + .pipe(Command.withDescription("Update all packages except Effect/Effect-App ecosystem packages")) - yield* Effect.logInfo(`Spawning child command: ${val}`) - const exitCode = yield* runGetExitCode(val) - if (exitCode !== 0) { - return yield* Effect.sync(() => process.exit(exitCode)) + const indexMulti = makeCommandWithWrap( + "index-multi", + {}, + Effect.fn("effa-cli.index-multi")(function*({}) { + yield* Effect.logInfo("Starting multi-index monitoring") + + const dirs = ["./api/src"] + + const existingDirs: string[] = [] + for (const dir of dirs) { + const dirExists = yield* fs.exists(dir) + if (dirExists) { + existingDirs.push(dir) + } else { + yield* Effect.logWarning(`Directory ${dir} does not exist - skipping`) } } - return - }, (_) => Effect.scoped(_)) - ) - - const EffectAppLibsPath = Args - .directory({ - exists: "yes", - name: "effect-app-libs-path" - }) - .pipe( - Args.withDefault("../../effect-app/libs"), - Args.withDescription("Path to the effect-app-libs directory") - ) - - const link = Command - .make( - "link", - { effectAppLibsPath: EffectAppLibsPath }, - Effect.fn("effa-cli.link")(function*({ effectAppLibsPath }) { - return yield* linkPackages(effectAppLibsPath) - }) + const monitors = existingDirs.map((dir) => monitorIndexes(dir)) + yield* Effect.all(monitors, { concurrency: monitors.length }) + }), + "Stopped multi-index monitoring" ) - .pipe(Command.withDescription("Link local effect-app packages using file resolutions")) + .pipe( + Command.withDescription( + "Monitor multiple directories for index and controller file changes" + ) + ) - const unlink = Command - .make( - "unlink", + const packagejson = makeCommandWithWrap( + "packagejson", {}, - Effect.fn("effa-cli.unlink")(function*({}) { - return yield* unlinkPackages - }) - ) - .pipe(Command.withDescription("Remove effect-app file resolutions and restore npm registry packages")) + Effect.fn("effa-cli.packagejson")(function*({}) { + // https://nodejs.org/api/path.html#pathresolvepaths + const startDir = path.resolve() - const ue = Command - .make( - "ue", - {}, - Effect.fn("effa-cli.ue")(function*({}) { - yield* Effect.logInfo("Update effect-app and/or effect packages") - - const prompted = yield* Prompt.select({ - choices: [ - { - title: "effect-app", - description: "Update only effect-app packages", - value: "effect-app" - }, - { - title: "effect", - description: "Update only effect packages", - value: "effect" - }, - { - title: "both", - description: "Update both effect-app and effect packages", - value: "both" - } - ], - message: "Select an option" - }) - - switch (prompted) { - case "effect-app": - return yield* updateEffectAppPackages.pipe( - Effect.andThen(runGetExitCode("pnpm i")) - ) - - case "effect": - return yield* updateEffectPackages.pipe( - Effect.andThen(runGetExitCode("pnpm i")) - ) - case "both": - return yield* updateEffectPackages.pipe( - Effect.andThen(updateEffectAppPackages), - Effect.andThen(runGetExitCode("pnpm i")) - ) - } - }) + return yield* monitorPackageJson(startDir, ".") + }), + "Stopped monitoring root package.json exports" ) - .pipe(Command.withDescription("Update effect-app and/or effect packages")) + .pipe( + Command.withDescription("Generate and update root-level package.json exports mappings for TypeScript modules") + ) - const up = Command - .make( - "up", + const packagejsonPackages = makeCommandWithWrap( + "packagejson-packages", {}, - Effect.fn("effa-cli.update-packages")(function*({}) { - yield* Effect.logInfo("Updating all packages except Effect/Effect-App ecosystem packages...") - - return yield* updatePackages.pipe( - Effect.andThen(runGetExitCode("pnpm i")) - ) - }) - ) - .pipe(Command.withDescription("Update all packages except Effect/Effect-App ecosystem packages")) + Effect.fn("effa-cli.packagejson-packages")(function*({}) { + // https://nodejs.org/api/path.html#pathresolvepaths + const startDir = path.resolve() - const indexMulti = makeCommandWithWrap( - "index-multi", - {}, - Effect.fn("effa-cli.index-multi")(function*({}) { - yield* Effect.logInfo("Starting multi-index monitoring") + const packagesDir = path.join(startDir, "packages") - const dirs = ["./api/src"] - - const existingDirs: string[] = [] - for (const dir of dirs) { - const dirExists = yield* fs.exists(dir) - if (dirExists) { - existingDirs.push(dir) - } else { - yield* Effect.logWarning(`Directory ${dir} does not exist - skipping`) + const packagesExists = yield* fs.exists(packagesDir) + if (!packagesExists) { + return yield* Effect.logWarning("No packages directory found") } - } - - const monitors = existingDirs.map((dir) => monitorIndexes(dir)) - yield* Effect.all(monitors, { concurrency: monitors.length }) - }), - "Stopped multi-index monitoring" - ) - .pipe( - Command.withDescription( - "Monitor multiple directories for index and controller file changes" - ) - ) - const packagejson = makeCommandWithWrap( - "packagejson", - {}, - Effect.fn("effa-cli.packagejson")(function*({}) { - // https://nodejs.org/api/path.html#pathresolvepaths - const startDir = path.resolve() + // get all package directories + const packageDirs = yield* fs.readDirectory(packagesDir) - return yield* monitorPackageJson(startDir, ".") - }), - "Stopped monitoring root package.json exports" - ) - .pipe( - Command.withDescription("Generate and update root-level package.json exports mappings for TypeScript modules") - ) + const validPackages: string[] = [] - const packagejsonPackages = makeCommandWithWrap( - "packagejson-packages", - {}, - Effect.fn("effa-cli.packagejson-packages")(function*({}) { - // https://nodejs.org/api/path.html#pathresolvepaths - const startDir = path.resolve() + // filter packages that have package.json and src directory + for (const packageName of packageDirs) { + const packagePath = path.join(packagesDir, packageName) + const packageJsonExists = yield* fs.exists(path.join(packagePath, "package.json")) + const srcExists = yield* fs.exists(path.join(packagePath, "src")) - const packagesDir = path.join(startDir, "packages") + const shouldExclude = false + || packageName.endsWith("eslint-codegen-model") + || packageName.endsWith("eslint-shared-config") + || packageName.endsWith("vue-components") - const packagesExists = yield* fs.exists(packagesDir) - if (!packagesExists) { - return yield* Effect.logWarning("No packages directory found") - } - - // get all package directories - const packageDirs = yield* fs.readDirectory(packagesDir) - - const validPackages: string[] = [] - - // filter packages that have package.json and src directory - for (const packageName of packageDirs) { - const packagePath = path.join(packagesDir, packageName) - const packageJsonExists = yield* fs.exists(path.join(packagePath, "package.json")) - const srcExists = yield* fs.exists(path.join(packagePath, "src")) - - const shouldExclude = false - || packageName.endsWith("eslint-codegen-model") - || packageName.endsWith("eslint-shared-config") - || packageName.endsWith("vue-components") - - if (packageJsonExists && srcExists && !shouldExclude) { - validPackages.push(packagePath) + if (packageJsonExists && srcExists && !shouldExclude) { + validPackages.push(packagePath) + } } - } - yield* Effect.logInfo(`Found ${validPackages.length} packages to update`) + yield* Effect.logInfo(`Found ${validPackages.length} packages to update`) - // update each package sequentially - yield* Effect.all( - validPackages.map( - Effect.fnUntraced(function*(packagePath) { - const relativePackagePath = path.relative(startDir, packagePath) - yield* Effect.logInfo(`Updating ${relativePackagePath}`) - return yield* monitorPackageJson(startDir, relativePackagePath) - }) + // update each package sequentially + yield* Effect.all( + validPackages.map( + Effect.fnUntraced(function*(packagePath) { + const relativePackagePath = path.relative(startDir, packagePath) + yield* Effect.logInfo(`Updating ${relativePackagePath}`) + return yield* monitorPackageJson(startDir, relativePackagePath) + }) + ) ) - ) - yield* Effect.logInfo("All packages updated successfully") - }), - "Stopped monitoring package.json exports for all packages" - ) - .pipe( - Command.withDescription("Generate and update package.json exports mappings for all packages in monorepo") - ) - - const gist = Command - .make( - "gist", - { - config: Options.file("config").pipe( - Options.withDefault("gists.yaml"), - Options.withDescription("Path to YAML configuration file") - ) - }, - Effect.fn("effa-cli.gist")(function*({ config }) { - return yield* GistHandler.handler({ - YAMLPath: config - }) - }) + yield* Effect.logInfo("All packages updated successfully") + }), + "Stopped monitoring package.json exports for all packages" ) - .pipe(Command.withDescription("Create GitHub gists from files specified in YAML configuration")) - - const nuke = Command - .make( - "nuke", - { - dryRun: Options.boolean("dry-run").pipe( - Options.withDescription("Show what would be done without making changes") - ), - storePrune: Options.boolean("store-prune").pipe( - Options.withDescription("Prune the package manager store") - ) - }, - Effect.fn("effa-cli.nuke")(function*({ dryRun, storePrune }) { - yield* Effect.logInfo(dryRun ? "Performing dry run cleanup..." : "Performing nuclear cleanup...") + .pipe( + Command.withDescription("Generate and update package.json exports mappings for all packages in monorepo") + ) - if (dryRun) { - yield* runGetExitCode( - "find . -depth \\( -type d \\( -name 'node_modules' -o -name '.nuxt' -o -name 'dist' -o -name '.output' -o -name '.nitro' -o -name '.cache' -o -name 'test-results' -o -name 'test-out' -o -name 'coverage' \\) -print \\) -o \\( -type f \\( -name '*.log' -o -name '*.tsbuildinfo' \\) -print \\)" + const gist = Command + .make( + "gist", + { + config: Flag.file("config").pipe( + Flag.withDefault("gists.yaml"), + Flag.withDescription("Path to YAML configuration file") ) - } else { - yield* runGetExitCode( - "find . -depth \\( -type d \\( -name 'node_modules' -o -name '.nuxt' -o -name 'dist' -o -name '.output' -o -name '.nitro' -o -name '.cache' -o -name 'test-results' -o -name 'test-out' -o -name 'coverage' \\) -exec rm -rf -- {} + \\) -o \\( -type f \\( -name '*.log' -o -name '*.tsbuildinfo' \\) -delete \\)" + }, + Effect.fn("effa-cli.gist")(function*({ config }) { + const gh = yield* GistHandler + return yield* gh.handler({ + YAMLPath: config + }) + }) + ) + .pipe(Command.withDescription("Create GitHub gists from files specified in YAML configuration")) + + const nuke = Command + .make( + "nuke", + { + dryRun: Flag.boolean("dry-run").pipe( + Flag.withDescription("Show what would be done without making changes") + ), + storePrune: Flag.boolean("store-prune").pipe( + Flag.withDescription("Prune the package manager store") ) + }, + Effect.fn("effa-cli.nuke")(function*({ dryRun, storePrune }) { + yield* Effect.logInfo(dryRun ? "Performing dry run cleanup..." : "Performing nuclear cleanup...") - if (storePrune) { + if (dryRun) { yield* runGetExitCode( - "pnpm store prune" + "find . -depth \\( -type d \\( -name 'node_modules' -o -name '.nuxt' -o -name 'dist' -o -name '.output' -o -name '.nitro' -o -name '.cache' -o -name 'test-results' -o -name 'test-out' -o -name 'coverage' \\) -print \\) -o \\( -type f \\( -name '*.log' -o -name '*.tsbuildinfo' \\) -print \\)" ) + } else { + yield* runGetExitCode( + "find . -depth \\( -type d \\( -name 'node_modules' -o -name '.nuxt' -o -name 'dist' -o -name '.output' -o -name '.nitro' -o -name '.cache' -o -name 'test-results' -o -name 'test-out' -o -name 'coverage' \\) -exec rm -rf -- {} + \\) -o \\( -type f \\( -name '*.log' -o -name '*.tsbuildinfo' \\) -delete \\)" + ) + + if (storePrune) { + yield* runGetExitCode( + "pnpm store prune" + ) + } } - } - yield* Effect.logInfo("Cleanup operation completed") - }) + yield* Effect.logInfo("Cleanup operation completed") + }) + ) + .pipe(Command.withDescription("Nuclear cleanup command: removes all generated files and cleans the workspace")) + + // configure CLI + return yield* Command.run( + Command + .make("effa") + .pipe(Command.withSubcommands([ + ue, + up, + link, + unlink, + indexMulti, + packagejson, + packagejsonPackages, + gist, + nuke + ])), + { + version: "v1.0.0" + } ) - .pipe(Command.withDescription("Nuclear cleanup command: removes all generated files and cleans the workspace")) - - // configure CLI - const cli = Command.run( - Command - .make("effa") - .pipe(Command.withSubcommands([ - ue, - up, - link, - unlink, - indexMulti, - packagejson, - packagejsonPackages, - gist, - nuke - ])), - { - name: "Effect-App CLI by jfet97 ❤️", - version: "v1.0.0" - } - ) - - return yield* cli(process.argv) - })() - .pipe( - Effect.scoped, - Effect.provide( - Layer.provideMerge( - Layer.merge( - GistHandler.Default, - RunCommandService.Default - ), - NodeContext.layer + })() + .pipe( + Effect.scoped, + Effect.provide( + Layer.provideMerge( + Layer.merge( + GistHandler.Default, + RunCommandService.Default + ), + NodeServices.layer + ) ) - ), - NodeRuntime.runMain - ) + ) +) diff --git a/packages/cli/src/os-command.ts b/packages/cli/src/os-command.ts index df07513de..b337f2e55 100644 --- a/packages/cli/src/os-command.ts +++ b/packages/cli/src/os-command.ts @@ -1,41 +1,33 @@ /* eslint-disable no-constant-binary-expression */ /* eslint-disable no-empty-pattern */ -// import necessary modules from the libraries -import { Command } from "@effect/platform" - -import { CommandExecutor } from "@effect/platform/CommandExecutor" -import { Effect, identity } from "effect" +import { Effect, Layer, ServiceMap } from "effect" +import { ChildProcess } from "effect/unstable/process" +import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner" /** * Service for executing shell commands using the Effect platform's Command API. * Provides methods to run shell commands with different output handling strategies. * All commands are executed through the system shell (/bin/sh) for proper command parsing. */ -// @effect-diagnostics-next-line missingEffectServiceDependency:off -export class RunCommandService extends Effect.Service()("RunCommandService", { - dependencies: [], - effect: Effect.gen(function*() { +export class RunCommandService extends ServiceMap.Service()("RunCommandService", { + make: Effect.gen(function*() { // will be provided by the main CLI pipeline setup - const commandExecutor = yield* CommandExecutor + const spawner = yield* ChildProcessSpawner /** * Executes a shell command using Command API with inherited stdio streams. - * The command is rn through the system shell (/bin/sh) for proper command parsing. + * The command is run through the system shell (/bin/sh) for proper command parsing. * * @param cmd - The shell command to execute * @param cwd - Optional working directory to execute the command in * @returns An Effect that succeeds with the exit code or fails with a PlatformError */ const runGetExitCode = (cmd: string, cwd?: string) => - Command - .make("sh", "-c", cmd) - .pipe( - Command.stdout("inherit"), - Command.stderr("inherit"), - cwd ? Command.workingDirectory(cwd) : identity, - Command.exitCode, - Effect.provideService(CommandExecutor, commandExecutor) + ChildProcess + .exitCode( + ChildProcess.make("sh", ["-c", cmd], { stdout: "inherit", stderr: "inherit", cwd }) ) + .pipe(Effect.provideService(ChildProcessSpawner, spawner)) /** * Executes a shell command using Command API and returns the output as a string. @@ -46,13 +38,11 @@ export class RunCommandService extends Effect.Service()("RunC * @returns An Effect that succeeds with the command's stdout output as string or fails with a PlatformError */ const runGetString = (cmd: string, cwd?: string) => - Command - .make("sh", "-c", cmd) - .pipe( - cwd ? Command.workingDirectory(cwd) : identity, - Command.string, - Effect.provideService(CommandExecutor, commandExecutor) + ChildProcess + .string( + ChildProcess.make("sh", ["-c", cmd], { cwd }) ) + .pipe(Effect.provideService(ChildProcessSpawner, spawner)) return { runGetExitCode, @@ -60,4 +50,5 @@ export class RunCommandService extends Effect.Service()("RunC } }) }) { + static Default = Layer.effect(this, this.make) } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c46a532e9..c88002909 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -54,7 +54,7 @@ importers: version: 2.29.8(@types/node@25.0.8) '@effect-app/cli': specifier: ^1.29.2 - version: 1.29.2(4ff7d73a3d5ba42ef17ca03fad555152) + version: 1.29.2(3de4b136a7aa665dc0b4e983e5d050c2) '@effect-app/eslint-codegen-model': specifier: workspace:* version: link:packages/eslint-codegen-model @@ -69,7 +69,7 @@ importers: version: 0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) '@effect/platform-node': specifier: ^0.104.0 - version: 0.104.0(@effect/cluster@0.56.1(ea0a85c9384965b815244e80e26b1417))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) + version: 0.104.0(@effect/cluster@0.56.1(e3b57098d6efa67930178f97e8546ff8))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(ioredis@5.9.3))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) '@effect/vitest': specifier: ^0.27.0 version: 0.27.0(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(vitest@4.0.17(@opentelemetry/api@1.9.0)(@types/node@25.0.8)(jsdom@27.4.0)(sass@1.97.2)(terser@5.45.0)(tsx@4.21.0)(yaml@2.8.2)) @@ -160,12 +160,12 @@ importers: packages/cli: dependencies: - '@effect/cli': - specifier: ^0.73.0 - version: 0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/printer-ansi@0.47.0(@effect/typeclass@0.38.0(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/printer@0.47.0(@effect/typeclass@0.38.0(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) '@effect/platform-node': - specifier: ^0.104.0 - version: 0.104.0(@effect/cluster@0.56.1(ea0a85c9384965b815244e80e26b1417))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) + specifier: ^4.0.0-beta.5 + version: 4.0.0-beta.5(effect@4.0.0-beta.5(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(ioredis@5.9.3) + effect: + specifier: ^4.0.0-beta.5 + version: 4.0.0-beta.5(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc) js-yaml: specifier: 4.1.1 version: 4.1.1 @@ -408,7 +408,7 @@ importers: dependencies: '@effect/experimental': specifier: ^0.58.0 - version: 0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) + version: 0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(ioredis@5.9.3) '@effect/platform': specifier: ^0.94.1 version: 0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) @@ -417,7 +417,7 @@ importers: version: 0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) '@effect/sql': specifier: ^0.49.0 - version: 0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) + version: 0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(ioredis@5.9.3))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) '@effect/vitest': specifier: ^0.27.0 version: 0.27.0(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(vitest@4.0.17(@opentelemetry/api@1.9.0)(@types/node@25.0.8)(jsdom@27.4.0)(sass@1.97.2)(terser@5.45.0)(tsx@4.21.0)(yaml@2.8.2)) @@ -523,10 +523,10 @@ importers: dependencies: '@effect-atom/atom': specifier: ^0.4.13 - version: 0.4.13(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) + version: 0.4.13(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(ioredis@5.9.3))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) '@effect-atom/atom-vue': specifier: ^0.4.5 - version: 0.4.5(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(vue@3.5.26(typescript@5.9.3(patch_hash=58625d8335f517e3d6974ef9faaff583d531ee7d0e9630b0ee4bab0fb5db70b0))) + version: 0.4.5(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(ioredis@5.9.3))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(vue@3.5.26(typescript@5.9.3(patch_hash=58625d8335f517e3d6974ef9faaff583d531ee7d0e9630b0ee4bab0fb5db70b0))) '@effect/platform': specifier: ^0.94.1 version: 0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) @@ -1117,36 +1117,43 @@ packages: resolution: {integrity: sha512-W+QD/4TkQKMPq02uz+ZIz/EI91YEtS2zHBu/Jihx2lfNh3HjUKgyY1KfqCTg2WGJVDkJtPc5KDw+M8yd4rI+Rg==} cpu: [arm64] os: [linux] + libc: [glibc] '@dprint/linux-arm64-musl@0.51.1': resolution: {integrity: sha512-P4Cfn8wrdSvdoMoz0HtsC5zZpbh8Kk3OjRzgG4qs/gub5Ba3CCLcVaQvQmIOCvgPzdhPB41eSn5qAK87mh7vCQ==} cpu: [arm64] os: [linux] + libc: [musl] '@dprint/linux-loong64-glibc@0.51.1': resolution: {integrity: sha512-dxDShELBrSGB9PktRqhYrm1UWCL+7N430rGuDTFg+i4d1wL4JD7EI5NDS/5IVbg3lrKJUu6gAJoY9NWF4sZ0mQ==} cpu: [loong64] os: [linux] + libc: [glibc] '@dprint/linux-loong64-musl@0.51.1': resolution: {integrity: sha512-l+uNPzUBNxkywGN1HgvTV0CzUDpzXC+s6VEvjYRdVAEKl9TAsIfLMfZifS8rnlOi9hz/0sckmpny+wL40G6dhQ==} cpu: [loong64] os: [linux] + libc: [musl] '@dprint/linux-riscv64-glibc@0.51.1': resolution: {integrity: sha512-YI6SemJRMsLUrtognhkAYOJ67j/AkiufHfx6+cPv/7qO6zBoBL6otR1e5yFYvAgEJOzRZD5g51EdqCcuK19B+g==} cpu: [riscv64] os: [linux] + libc: [glibc] '@dprint/linux-x64-glibc@0.51.1': resolution: {integrity: sha512-Hm9ntOWeclXU6/rZGYIN2XjuAhsRUZ77lRJC5EiM1rP6Ayh0J/YMsDdqlPAs+xZGdFFzt8ZeUpqJJJsbc54ZjQ==} cpu: [x64] os: [linux] + libc: [glibc] '@dprint/linux-x64-musl@0.51.1': resolution: {integrity: sha512-iUs/9Z4Ky6+2GAK+i1jF1ZwM27CSIGaqw3LxTXtmE8eBCRwyCW+opzn9bzaFtlT9REUAGMC5PXihAPmzP3ttWg==} cpu: [x64] os: [linux] + libc: [musl] '@dprint/typescript@0.95.13': resolution: {integrity: sha512-lzhaiENYcsLOo+qJHzwWbZ/Ne+67eDGLwhU52Z0AnW6gHeCN1EZ8DDe5gI/f/Fvk2Jd2wlnmoyrsrYnkg/SDgw==} @@ -1228,6 +1235,12 @@ packages: '@effect/sql': ^0.49.0 effect: ^3.19.13 + '@effect/platform-node-shared@4.0.0-beta.5': + resolution: {integrity: sha512-tXgq+qQ/6Zk8MdQ2H4Xu2ui1SZDkkbnuoxMv4MTFgnZwZsALCcgDtP5mtl6t4T0nJncqqvbIsaotFAQXs0TEWA==} + engines: {node: '>=18.0.0'} + peerDependencies: + effect: ^4.0.0-beta.5 + '@effect/platform-node@0.104.0': resolution: {integrity: sha512-2ZkUDDTxLD95ARdYIKBx4tdIIgqA3cwb3jlnVVBxmHUf0Pg5N2HdMuD0Q+CXQ7Q94FDwnLW3ZvaSfxDh6FvrNw==} peerDependencies: @@ -1237,6 +1250,13 @@ packages: '@effect/sql': ^0.49.0 effect: ^3.19.13 + '@effect/platform-node@4.0.0-beta.5': + resolution: {integrity: sha512-quaHbE5sYsNbkvw//cWynWGf1AhnvwTZKVlciv+atyJlA9ISSMzf46SH8Z4f9MhoR97GWBe4vr5Bj6cx696ZIA==} + engines: {node: '>=18.0.0'} + peerDependencies: + effect: ^4.0.0-beta.5 + ioredis: ^5.7.0 + '@effect/platform@0.94.1': resolution: {integrity: sha512-SlL8OMTogHmMNnFLnPAHHo3ua1yrB1LNQOVQMiZsqYu9g3216xjr0gn5WoDgCxUyOdZcseegMjWJ7dhm/2vnfg==} peerDependencies: @@ -1718,6 +1738,9 @@ packages: '@types/node': optional: true + '@ioredis/commands@1.5.0': + resolution: {integrity: sha512-eUgLqrMf8nJkZxT24JvVRrQya1vZkQh8BBeYNwGDqa5I0VUi8ACx7uFvAaLxintokpTenkK6DASvo/bvNbBGow==} + '@isaacs/balanced-match@4.0.1': resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} engines: {node: 20 || >=22} @@ -2036,36 +2059,42 @@ packages: engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm-musl@2.5.4': resolution: {integrity: sha512-kGO8RPvVrcAotV4QcWh8kZuHr9bXi9a3bSZw7kFarYR0+fGliU7hd/zevhjw8fnvIKG3J9EO5G6sXNGCSNMYPQ==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [musl] '@parcel/watcher-linux-arm64-glibc@2.5.4': resolution: {integrity: sha512-KU75aooXhqGFY2W5/p8DYYHt4hrjHZod8AhcGAmhzPn/etTa+lYCDB2b1sJy3sWJ8ahFVTdy+EbqSBvMx3iFlw==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm64-musl@2.5.4': resolution: {integrity: sha512-Qx8uNiIekVutnzbVdrgSanM+cbpDD3boB1f8vMtnuG5Zau4/bdDbXyKwIn0ToqFhIuob73bcxV9NwRm04/hzHQ==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [musl] '@parcel/watcher-linux-x64-glibc@2.5.4': resolution: {integrity: sha512-UYBQvhYmgAv61LNUn24qGQdjtycFBKSK3EXr72DbJqX9aaLbtCOO8+1SkKhD/GNiJ97ExgcHBrukcYhVjrnogA==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-x64-musl@2.5.4': resolution: {integrity: sha512-YoRWCVgxv8akZrMhdyVi6/TyoeeMkQ0PGGOf2E4omODrvd1wxniXP+DBynKoHryStks7l+fDAMUBRzqNHrVOpg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [musl] '@parcel/watcher-win32-arm64@2.5.4': resolution: {integrity: sha512-iby+D/YNXWkiQNYcIhg8P5hSjzXEHaQrk2SLrWOUD7VeC4Ohu0WQvmV+HDJokZVJ2UjJ4AGXW3bx7Lls9Ln4TQ==} @@ -2156,66 +2185,79 @@ packages: resolution: {integrity: sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.55.1': resolution: {integrity: sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.55.1': resolution: {integrity: sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.55.1': resolution: {integrity: sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.55.1': resolution: {integrity: sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.55.1': resolution: {integrity: sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==} cpu: [loong64] os: [linux] + libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.55.1': resolution: {integrity: sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.55.1': resolution: {integrity: sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==} cpu: [ppc64] os: [linux] + libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.55.1': resolution: {integrity: sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.55.1': resolution: {integrity: sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.55.1': resolution: {integrity: sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.55.1': resolution: {integrity: sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.55.1': resolution: {integrity: sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openbsd-x64@4.55.1': resolution: {integrity: sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==} @@ -2645,6 +2687,9 @@ packages: '@types/whatwg-url@13.0.0': resolution: {integrity: sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==} + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} @@ -2756,41 +2801,49 @@ packages: resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} cpu: [arm64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-arm64-musl@1.11.1': resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} cpu: [arm64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} cpu: [riscv64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} cpu: [riscv64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} cpu: [s390x] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-gnu@1.11.1': resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} cpu: [x64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-musl@1.11.1': resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} cpu: [x64] os: [linux] + libc: [musl] '@unrs/resolver-binding-wasm32-wasi@1.11.1': resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} @@ -3418,6 +3471,10 @@ packages: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} + cluster-key-slot@1.1.2: + resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} + engines: {node: '>=0.10.0'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -3616,6 +3673,10 @@ packages: resolution: {integrity: sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==} engines: {node: '>=0.10'} + denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -3742,6 +3803,9 @@ packages: effect@3.19.14: resolution: {integrity: sha512-3vwdq0zlvQOxXzXNKRIPKTqZNMyGCdaFUBfMPqpsyzZDre67kgC1EEHDV4EoQTovJ4w5fmJW756f86kkuz7WFA==} + effect@4.0.0-beta.5: + resolution: {integrity: sha512-M78LYkzjnfUgYvdNF/CoLF9gK74zEORHd0pSoSa/p2BQNJhe7fuOyksddBaZULfJeajzTEOKyRfymsBdaxKA7w==} + electron-to-chromium@1.5.267: resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} @@ -4434,6 +4498,10 @@ packages: resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + ini@6.0.0: + resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==} + engines: {node: ^20.17.0 || >=22.9.0} + internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} @@ -4453,6 +4521,10 @@ packages: peerDependencies: fp-ts: ^2.5.0 + ioredis@5.9.3: + resolution: {integrity: sha512-VI5tMCdeoxZWU5vjHWsiE/Su76JGhBvWF1MJnV9ZtGltHk9BmD48oDq8Tj8haZ85aceXZMxLNDQZRVo5QKNgXA==} + engines: {node: '>=12.22.0'} + ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} @@ -4821,6 +4893,12 @@ packages: lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + lodash.defaults@4.2.0: + resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + + lodash.isarguments@3.1.0: + resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} + lodash.isempty@4.4.0: resolution: {integrity: sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==} @@ -4975,6 +5053,11 @@ packages: engines: {node: '>=10.0.0'} hasBin: true + mime@4.1.0: + resolution: {integrity: sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==} + engines: {node: '>=16'} + hasBin: true + mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -5930,6 +6013,9 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + standard-as-callback@2.1.0: + resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} + statuses@2.0.2: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} @@ -6290,6 +6376,10 @@ packages: resolution: {integrity: sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw==} engines: {node: '>=20.18.1'} + undici@7.22.0: + resolution: {integrity: sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==} + engines: {node: '>=20.18.1'} + unimport@5.6.0: resolution: {integrity: sha512-8rqAmtJV8o60x46kBAJKtHpJDJWkA2xcBqWKPI14MgUb05o1pnpnCnXSxedUXyeq7p8fR5g3pTo2BaswZ9lD9A==} engines: {node: '>=18.12.0'} @@ -7447,10 +7537,10 @@ snapshots: '@dprint/win32-x64@0.51.1': optional: true - '@effect-app/cli@1.29.2(4ff7d73a3d5ba42ef17ca03fad555152)': + '@effect-app/cli@1.29.2(3de4b136a7aa665dc0b4e983e5d050c2)': dependencies: '@effect/cli': 0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/printer-ansi@0.47.0(@effect/typeclass@0.38.0(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/printer@0.47.0(@effect/typeclass@0.38.0(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) - '@effect/platform-node': 0.104.0(@effect/cluster@0.56.1(ea0a85c9384965b815244e80e26b1417))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) + '@effect/platform-node': 0.104.0(@effect/cluster@0.56.1(e3b57098d6efa67930178f97e8546ff8))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(ioredis@5.9.3))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) js-yaml: 4.1.1 node-watch: 0.7.4 transitivePeerDependencies: @@ -7464,9 +7554,9 @@ snapshots: - effect - utf-8-validate - '@effect-atom/atom-vue@0.4.5(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(vue@3.5.26(typescript@5.9.3(patch_hash=58625d8335f517e3d6974ef9faaff583d531ee7d0e9630b0ee4bab0fb5db70b0)))': + '@effect-atom/atom-vue@0.4.5(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(ioredis@5.9.3))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(vue@3.5.26(typescript@5.9.3(patch_hash=58625d8335f517e3d6974ef9faaff583d531ee7d0e9630b0ee4bab0fb5db70b0)))': dependencies: - '@effect-atom/atom': 0.4.13(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) + '@effect-atom/atom': 0.4.13(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(ioredis@5.9.3))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) effect: 3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc) vue: 3.5.26(typescript@5.9.3(patch_hash=58625d8335f517e3d6974ef9faaff583d531ee7d0e9630b0ee4bab0fb5db70b0)) transitivePeerDependencies: @@ -7474,9 +7564,9 @@ snapshots: - '@effect/platform' - '@effect/rpc' - '@effect-atom/atom@0.4.13(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))': + '@effect-atom/atom@0.4.13(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(ioredis@5.9.3))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))': dependencies: - '@effect/experimental': 0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) + '@effect/experimental': 0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(ioredis@5.9.3) '@effect/platform': 0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) '@effect/rpc': 0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) effect: 3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc) @@ -7491,20 +7581,22 @@ snapshots: toml: 3.0.0 yaml: 2.8.2 - '@effect/cluster@0.56.1(ea0a85c9384965b815244e80e26b1417)': + '@effect/cluster@0.56.1(e3b57098d6efa67930178f97e8546ff8)': dependencies: '@effect/platform': 0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) '@effect/rpc': 0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) - '@effect/sql': 0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) - '@effect/workflow': 0.16.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) + '@effect/sql': 0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(ioredis@5.9.3))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) + '@effect/workflow': 0.16.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(ioredis@5.9.3))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) effect: 3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc) kubernetes-types: 1.30.0 - '@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))': + '@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(ioredis@5.9.3)': dependencies: '@effect/platform': 0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) effect: 3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc) uuid: 11.1.0 + optionalDependencies: + ioredis: 5.9.3 '@effect/language-service@0.71.2': {} @@ -7514,12 +7606,12 @@ snapshots: effect: 3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc) multipasta: 0.2.7 - '@effect/platform-node-shared@0.57.0(@effect/cluster@0.56.1(ea0a85c9384965b815244e80e26b1417))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))': + '@effect/platform-node-shared@0.57.0(@effect/cluster@0.56.1(e3b57098d6efa67930178f97e8546ff8))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(ioredis@5.9.3))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))': dependencies: - '@effect/cluster': 0.56.1(ea0a85c9384965b815244e80e26b1417) + '@effect/cluster': 0.56.1(e3b57098d6efa67930178f97e8546ff8) '@effect/platform': 0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) '@effect/rpc': 0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) - '@effect/sql': 0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) + '@effect/sql': 0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(ioredis@5.9.3))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) '@parcel/watcher': 2.5.4 effect: 3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc) multipasta: 0.2.7 @@ -7528,13 +7620,22 @@ snapshots: - bufferutil - utf-8-validate - '@effect/platform-node@0.104.0(@effect/cluster@0.56.1(ea0a85c9384965b815244e80e26b1417))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))': + '@effect/platform-node-shared@4.0.0-beta.5(effect@4.0.0-beta.5(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))': + dependencies: + '@types/ws': 8.18.1 + effect: 4.0.0-beta.5(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc) + ws: 8.19.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@effect/platform-node@0.104.0(@effect/cluster@0.56.1(e3b57098d6efa67930178f97e8546ff8))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(ioredis@5.9.3))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))': dependencies: - '@effect/cluster': 0.56.1(ea0a85c9384965b815244e80e26b1417) + '@effect/cluster': 0.56.1(e3b57098d6efa67930178f97e8546ff8) '@effect/platform': 0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) - '@effect/platform-node-shared': 0.57.0(@effect/cluster@0.56.1(ea0a85c9384965b815244e80e26b1417))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) + '@effect/platform-node-shared': 0.57.0(@effect/cluster@0.56.1(e3b57098d6efa67930178f97e8546ff8))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(ioredis@5.9.3))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) '@effect/rpc': 0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) - '@effect/sql': 0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) + '@effect/sql': 0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(ioredis@5.9.3))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) effect: 3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc) mime: 3.0.0 undici: 7.18.2 @@ -7543,6 +7644,17 @@ snapshots: - bufferutil - utf-8-validate + '@effect/platform-node@4.0.0-beta.5(effect@4.0.0-beta.5(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(ioredis@5.9.3)': + dependencies: + '@effect/platform-node-shared': 4.0.0-beta.5(effect@4.0.0-beta.5(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) + effect: 4.0.0-beta.5(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc) + ioredis: 5.9.3 + mime: 4.1.0 + undici: 7.22.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + '@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))': dependencies: effect: 3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc) @@ -7567,9 +7679,9 @@ snapshots: effect: 3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc) msgpackr: 1.11.8 - '@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))': + '@effect/sql@0.49.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(ioredis@5.9.3))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))': dependencies: - '@effect/experimental': 0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) + '@effect/experimental': 0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(ioredis@5.9.3) '@effect/platform': 0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) effect: 3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc) uuid: 11.1.0 @@ -7583,9 +7695,9 @@ snapshots: effect: 3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc) vitest: 4.0.17(@opentelemetry/api@1.9.0)(@types/node@25.0.8)(jsdom@27.4.0)(sass@1.97.2)(terser@5.45.0)(tsx@4.21.0)(yaml@2.8.2) - '@effect/workflow@0.16.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))': + '@effect/workflow@0.16.0(@effect/experimental@0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(ioredis@5.9.3))(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(@effect/rpc@0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))': dependencies: - '@effect/experimental': 0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) + '@effect/experimental': 0.58.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc))(ioredis@5.9.3) '@effect/platform': 0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) '@effect/rpc': 0.73.0(@effect/platform@0.94.1(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)))(effect@3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc)) effect: 3.19.14(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc) @@ -7885,6 +7997,8 @@ snapshots: optionalDependencies: '@types/node': 25.0.8 + '@ioredis/commands@1.5.0': {} + '@isaacs/balanced-match@4.0.1': {} '@isaacs/brace-expansion@5.0.0': @@ -8912,6 +9026,10 @@ snapshots: dependencies: '@types/webidl-conversions': 7.0.3 + '@types/ws@8.18.1': + dependencies: + '@types/node': 25.0.8 + '@types/yargs-parser@21.0.3': {} '@types/yargs@15.0.20': @@ -9784,6 +9902,8 @@ snapshots: clone@1.0.4: {} + cluster-key-slot@1.1.2: {} + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -9958,6 +10078,8 @@ snapshots: denque@1.5.1: {} + denque@2.1.0: {} + depd@2.0.0: {} dependency-tree@11.2.0: @@ -10097,6 +10219,19 @@ snapshots: '@standard-schema/spec': 1.1.0 fast-check: 4.5.3 + effect@4.0.0-beta.5(patch_hash=a12dc8f2d296f82664b0f522ff021a5c04c3c12eaac619a0db5269d34acd22cc): + dependencies: + '@standard-schema/spec': 1.1.0 + fast-check: 4.5.3 + find-my-way-ts: 0.1.6 + ini: 6.0.0 + kubernetes-types: 1.30.0 + msgpackr: 1.11.8 + multipasta: 0.2.7 + toml: 3.0.0 + uuid: 13.0.0 + yaml: 2.8.2 + electron-to-chromium@1.5.267: {} emoji-regex-xs@1.0.0: {} @@ -11009,6 +11144,8 @@ snapshots: ini@4.1.3: {} + ini@6.0.0: {} + internal-slot@1.1.0: dependencies: es-errors: 1.3.0 @@ -11033,6 +11170,20 @@ snapshots: dependencies: fp-ts: 2.16.11 + ioredis@5.9.3: + dependencies: + '@ioredis/commands': 1.5.0 + cluster-key-slot: 1.1.2 + debug: 4.4.3(supports-color@5.5.0) + denque: 2.1.0 + lodash.defaults: 4.2.0 + lodash.isarguments: 3.1.0 + redis-errors: 1.2.0 + redis-parser: 3.0.0 + standard-as-callback: 2.1.0 + transitivePeerDependencies: + - supports-color + ipaddr.js@1.9.1: {} is-arguments@1.2.0: @@ -11403,6 +11554,10 @@ snapshots: lodash.debounce@4.0.8: {} + lodash.defaults@4.2.0: {} + + lodash.isarguments@3.1.0: {} + lodash.isempty@4.4.0: {} lodash.isequal@4.5.0: {} @@ -11544,6 +11699,8 @@ snapshots: mime@3.0.0: {} + mime@4.1.0: {} + mimic-fn@2.1.0: {} min-indent@1.0.1: {} @@ -12601,6 +12758,8 @@ snapshots: stackback@0.0.2: {} + standard-as-callback@2.1.0: {} + statuses@2.0.2: {} std-env@3.10.0: {} @@ -12984,6 +13143,8 @@ snapshots: undici@7.18.2: {} + undici@7.22.0: {} + unimport@5.6.0: dependencies: acorn: 8.15.0 diff --git a/task/Migration/01-cli.md b/task/Migration/01-cli.md new file mode 100644 index 000000000..a0d38b90a --- /dev/null +++ b/task/Migration/01-cli.md @@ -0,0 +1,61 @@ +# Step 1: `@effect-app/cli` Migration to Effect v4 + +## Status: Complete + +## Files Changed + +- `packages/cli/package.json` — updated dependencies +- `packages/cli/src/extract.ts` — minor API fix +- `packages/cli/src/gist.ts` — major API changes +- `packages/cli/src/index.ts` — CLI and platform API changes +- `packages/cli/src/os-command.ts` — service class + process API migration + +## Changes Made + +### package.json +- Removed `@effect/cli`, `@effect/platform`, `@effect/platform-node` +- Added `effect: ^4.0.0-beta.5` +- Updated `@effect/platform-node` to `^4.0.0-beta.5` + +### os-command.ts +- `Effect.Service` → `ServiceMap.Service` (now in `effect` directly, not `@effect/platform`) +- `Command` / `CommandExecutor` → `ChildProcess` from `effect/unstable/process` +- `ChildProcessSpawner` from `effect/unstable/process/ChildProcessSpawner` +- Removed stale `@effect-diagnostics-next-line missingEffectServiceDependency:off` comment (rule renamed in v4) + +### extract.ts +- `Order.string` → `Order.String` (capitalized in v4) + +### gist.ts +- `ParseResult` removed from imports; added `SchemaIssue`, `SchemaTransformation` +- `GistEntryDecoded` class using `GistEntry.transformOrFail<>()` (v3 Schema.Class API, removed in v4) → + replaced with `const GistEntryDecoded = GistEntry.pipe(Schema.decodeTo(targetStruct, SchemaTransformation.transformOrFail({decode, encode})))` + and `export interface GistEntryDecoded extends Schema.Schema.Type {}` +- Inside decode function: `ParseResult.Composite` / `ParseResult.Type` → `SchemaIssue.InvalidValue(Option.some(value), { message })` +- `Array.isNonEmptyArray` → `Array.isArrayNonEmpty` +- `Schema.optionalWith({ default: () => ({}), nullable: true, exact: true })` → + `Schema.optional(Schema.NullOr(schema))` + `?? {}` at usage site +- `Schema.Record({ key: K, value: V })` → `Schema.Record(K, V)` (args changed in v4) +- `Effect.catchAll((e) => Effect.dieMessage(...))` → + `Effect.mapError((e) => new Error(...)).pipe(Effect.orDie)` +- `Effect.dieMessage("msg")` → `Effect.die(new Error("msg"))` +- `Effect.orElse(() => fallback)` → removed (was dead code after using suppressed helpers) +- `Schema.parseJson(S)` → `Schema.fromJsonString(S)` +- `Schema.decodeUnknown(S)` → `Schema.decodeUnknownEffect(S)` +- `Schema.encodeUnknown(S)` → `Schema.encodeUnknownEffect(S)` +- `Effect.all({ company: Config.string(...), env: Config.string(...).pipe(Config.withDefault("local-dev")) })` → + separate `yield* Config.string(...)` calls + `Config.withDefault(() => "local-dev")` (now takes `LazyArg`) +- `Array.filterMap(arr, fn)` → native `arr.flatMap(fn)` +- `Object.entries(configFromYaml.gists)` → `Object.entries(configFromYaml.gists ?? {})` +- Removed stale `@effect-diagnostics-next-line missingEffectServiceDependency:off` comment + +### index.ts +- `Args` → `Argument` (renamed in v4 CLI) +- `Options` → `Flag` (renamed in v4 CLI) +- `fs.watch(path, { recursive: true })` → `fs.watch(path)` (no options in v4) +- `Command.Config` namespace not exported → changed `makeCommandWithWrap` to use unconstrained `Config` generic and `any` handler param +- `NodeRuntime.runMain` no longer pipeable → wrap entire effect in `NodeRuntime.runMain(...)` call + +## Findings + +See `task/findings.md` for all v3→v4 API mapping findings. diff --git a/task/findings.md b/task/findings.md new file mode 100644 index 000000000..0c9737cd0 --- /dev/null +++ b/task/findings.md @@ -0,0 +1,104 @@ +# Effect v3 → v4 Migration Findings + +## Package Changes + +| v3 | v4 | +|---|---| +| `@effect/cli` (separate package) | `effect/unstable/cli` (built into `effect`) | +| `@effect/platform` (separate package) | Built into `effect` | +| `@effect/platform-node` (peer deps) | `@effect/platform-node ^4.0.0-beta.5` | + +## Service Classes + +| v3 | v4 | +|---|---| +| `Effect.Service()("Tag", { dependencies: [...], effect: Effect.gen(...) })` | `ServiceMap.Service()("Tag", { make: Effect.gen(...) })` with static `Default` and `DefaultWithoutDependencies` | +| `Effect.Service` imported from `effect` | `ServiceMap` imported from `effect` | + +Example migration: +```ts +// v3 +class MyService extends Effect.Service()("MyService", { + dependencies: [Dep.Default], + effect: Effect.gen(function*() { ... }) +}) {} + +// v4 +class MyService extends ServiceMap.Service()("MyService", { + make: Effect.gen(function*() { ... }) +}) { + static DefaultWithoutDependencies = Layer.effect(this, this.make) + static Default = this.DefaultWithoutDependencies.pipe( + Layer.provide(Dep.Default) + ) +} +``` + +## Schema API + +| v3 | v4 | +|---|---| +| `Schema.parseJson(schema)` | `Schema.fromJsonString(schema)` | +| `Schema.decodeUnknown(schema)` | `Schema.decodeUnknownEffect(schema)` | +| `Schema.encodeUnknown(schema)` | `Schema.encodeUnknownEffect(schema)` | +| `Schema.optionalWith({ default: () => x, nullable: true, exact: true })` | `Schema.optional(Schema.NullOr(schema))` + `?? default` at usage | +| `Schema.Record({ key: K, value: V })` | `Schema.Record(K, V)` (positional args) | +| `Schema.Class.transformOrFail("T")({fields}, {decode, encode})` | `sourceSchema.pipe(Schema.decodeTo(targetStruct, SchemaTransformation.transformOrFail({decode, encode})))` + `export type T = Schema.Schema.Type` | +| `ParseResult.Type(ast, value, msg)` | `SchemaIssue.InvalidValue(Option.some(value), { message: msg })` | +| `ParseResult.Composite(ast, value, issues)` | `SchemaIssue.Composite(ast, Option.some(value), issues)` (ast still needed) | +| `ParseResult.succeed(x)` | `Effect.succeed(x)` | +| `Array.isNonEmptyArray` | `Array.isArrayNonEmpty` | + +## Effect API + +| v3 | v4 | +|---|---| +| `Effect.dieMessage("msg")` | `Effect.die(new Error("msg"))` | +| `Effect.catchAll((e) => Effect.dieMessage(...))` | `Effect.mapError((e) => new Error(...)).pipe(Effect.orDie)` | +| `Effect.orElse(() => fallback)` | `Effect.catchCause(() => fallback)` | +| `Effect.all({ a: Config.string(...) })` | Separate `yield* Config.string(...)` calls | +| `Config.withDefault("value")` | `Config.withDefault(() => "value")` (now takes `LazyArg`) | + +## Order Module + +| v3 | v4 | +|---|---| +| `Order.string` | `Order.String` (capitalized) | +| `Order.number` | `Order.Number` (capitalized) | + +## Platform APIs + +| v3 | v4 | +|---|---| +| `Command` from `@effect/platform` | `ChildProcess` from `effect/unstable/process` | +| `CommandExecutor` | `ChildProcessSpawner` from `effect/unstable/process/ChildProcessSpawner` | +| `command.string()` | `ChildProcess.string(ChildProcess.make(...))` | +| `command.exitCode()` | `ChildProcess.exitCode(ChildProcess.make(...))` | +| `FileSystem.FileSystem.watch(path, { recursive: true })` | `FileSystem.FileSystem.watch(path)` (no options object) | + +## CLI (from `@effect/cli` → `effect/unstable/cli`) + +| v3 | v4 | +|---|---| +| `Args` | `Argument` | +| `Options` | `Flag` | +| `Command.Config` namespace (type) | Not exported — use unconstrained generics or `any` | + +## Runtime + +| v3 | v4 | +|---|---| +| `NodeRuntime.runMain` used as last `.pipe()` argument | Must call `NodeRuntime.runMain(effect)` directly | + +## TypeScript Plugin Directives + +- `@effect-diagnostics-next-line missingEffectServiceDependency:off` — rule renamed or removed in v4, remove stale comments + +## Imports + +Most `@effect/*` sub-packages are now consolidated into `effect`: +- `import { ServiceMap } from "effect"` +- `import { SchemaTransformation, SchemaIssue } from "effect"` +- `import { ChildProcess } from "effect/unstable/process"` +- `import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner"` +- CLI: `import { Argument, Command, Flag, Prompt } from "effect/unstable/cli"` From 63b263e76b743c8dd18e96c7c8f8dc3747b653c3 Mon Sep 17 00:00:00 2001 From: Patrick Roza Date: Thu, 19 Feb 2026 13:28:39 +0100 Subject: [PATCH 14/45] update instructions --- AGENTS.md | 9 +++++++++ task/Migrate_to_Effect_v4.md | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index cf4e809d7..4be9410b5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -64,6 +64,15 @@ class MyService extends ServiceMap.Service()("MyService") {} ``` +## Checking Array is not empty + +Avoid `.length > 0` or `.length === 0` or `!.length` or `!!.length` checks, use `Array.isArrayNonEmpty` for type narrowing by default. + +## Filtering and Mapping + +Use Effect's `Array.filter()` with a `Filter.Filter` to do both the filtering and mapping. + + - Run type checking: `pnpm build` @@ -25,6 +27,13 @@ This is the Effect App library repository, focusing on functional programming pa +#### Migrations + +- Run `pnpm eslint fix ./src/` inside the package root after editing files +- Run type checking: `pnpm tsc --noEmit ./src/` inside the package root after editing files + - If type checking continues to fail, run `pnpm clean` to clear caches, then re-run `pnpm tsc ./src/` + + ## Code Style Guidelines **Always** look at existing code in the repository to learn and follow diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..8359239d2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,3 @@ +# CLAUDE.md + +Strictly follow the rules in ./AGENTS.md diff --git a/task/Migrate_to_Effect_v4.md b/task/Migrate_to_Effect_v4.md index e3df178d8..d09f294bf 100644 --- a/task/Migrate_to_Effect_v4.md +++ b/task/Migrate_to_Effect_v4.md @@ -10,6 +10,7 @@ There are migration guides: ## Steps +0. First upgrade/replace all effect v3 packages with v4 counterparts in the repository. Make sure no references to v3 packages remain. Remove v3 patches. 1. Convert `cli` - as it's a standalone utility using minimal effect libraries. 2. Convert `effect-app` core 3. Convert `infra` @@ -23,14 +24,20 @@ Commit every task you complete for every step. ## Rules - Always check `AGENTS.md` in the root of each repository to understand rules. -- You're allowed to use different versions of workspace packages within a project for the duration of the migration. + - Ignore the `#### New Features` section, instead follow `#### Migrations` for `### Mandatory Validation Steps` - Create task files for each Step in markdown files under `task/Migration` directory, and track progress and findings in each. - Save all conversion findings in a `task/findings.md` file to speed up future migrations. Read this file for every step! - Never replace any function argument type with `any` -- Never cast to any as a "fix" (`(s as any)`)! nor recasting via `unknown`: e.g `as unknown as S.Schema`. Maybe you first need to fix other files. +- Never cast to `any` as a "fix" (`(s as any)`)! nor recasting via `any` e.g `as any as S.Schema`. or `unknown`: e.g `as unknown as S.Schema`. Maybe you first need to fix other files. - Consult the migration guides instead of making up assumptions. e.g `Schema` is now `Codec` - Prioritise first fixing files that are dependencies of others (via direct or indirect imports). - Migrate and fix files in dependency order + +## Process + +- Always consult `findings.md` to help with migration or to fix build errors. +- When not finding the solution there, inspect the migration guides (this file, and the migration guides listed at the top), and source code in the `repos` folder +- Once finding a new solution, or fix mistakes, update `findings.md` ## Conversion @@ -82,7 +89,7 @@ class GHGistService extends ServiceMap.Service()("GHGistService", ## Context -- The effect repo is located inside `/repos/effect` -- The effect-smol repo is located inside `/repos/effect-smol` +- The effect repo is located inside `repos/effect` +- The effect-smol repo is located inside `repos/effect-smol` All repos can be kept uptodate with `git submodule foreach git pull origin main` and `git submodule foreach pnpm i`. diff --git a/task/findings.md b/task/findings.md index c8b9328c8..44fb0b7b7 100644 --- a/task/findings.md +++ b/task/findings.md @@ -41,6 +41,8 @@ class MyService extends ServiceMap.Service()("MyService", { | `Schema.parseJson(schema)` | `Schema.fromJsonString(schema)` | | `Schema.decodeUnknown(schema)` | `Schema.decodeUnknownEffect(schema)` | | `Schema.encodeUnknown(schema)` | `Schema.encodeUnknownEffect(schema)` | +| `S.encode(schema)` | `S.encodeEffect(schema)` — returns curried `(value) => Effect` | +| `S.decode(schema)` | `S.decodeEffect(schema)` — returns curried `(encoded) => Effect` | | `Schema.optionalWith({ default: () => x, nullable: true, exact: true })` | `Schema.optional(Schema.NullOr(schema))` + `?? default` at usage | | `Schema.Record({ key: K, value: V })` | `Schema.Record(K, V)` (positional args) | | `Schema.Class.transformOrFail("T")({fields}, {decode, encode})` | `sourceSchema.pipe(Schema.decodeTo(targetStruct, SchemaTransformation.transformOrFail({decode, encode})))` — keep as class with `Schema.Opaque()(schema)` | @@ -50,6 +52,10 @@ class MyService extends ServiceMap.Service()("MyService", { | `ParseResult.Composite(ast, value, issues)` | `SchemaIssue.Composite(ast, Option.some(value), issues)` (ast still needed) | | `ParseResult.succeed(x)` | `Effect.succeed(x)` | | `Array.isNonEmptyArray` | `Array.isArrayNonEmpty` | +| `S.Schema` (3 type params — schema with requirements) | `S.Codec` — **IMPORTANT**: in v4, a schema with context/service requirements is `Codec`, not `Schema`. `Schema` is always 2-param. **Never remove the R param — change `Schema` to `Codec` instead.** | +| `S.ParseResult.ParseError` | `S.SchemaError` | +| `schema.pipe(S.pick("field1", "field2"))` | `S.pick` removed. For Struct schemas: `(schema as Struct).mapFields(({ field1, field2 }) => ({ field1, field2 }))`. Or access `schema.fields` to create a new struct: `S.Struct({ field: schema.fields.field })` | +| `ast._tag === "Transformation"` | `"Transformation"` tag removed from AST. v4 AST tags are: `"Declaration"`, `"Objects"`, `"Arrays"`, `"Union"`, `"Filter"`, `"FilterGroup"`, plus primitive tags. | ## Effect API @@ -60,6 +66,12 @@ class MyService extends ServiceMap.Service()("MyService", { | `Effect.orElse(() => fallback)` | `Effect.catchCause(() => fallback)` | | `Effect.all({ a: Config.string(...) })` | `Config.all({ a: Config.string(...) })` — use module's own `.all()` for Config/Either/Option | | `Config.withDefault("value")` | `Config.withDefault(() => "value")` (now takes `LazyArg`) | +| `Effect.either(effect)` | `Effect.result(effect)` — returns `Result` not `Either` | +| `Effect.catchAllCause(handler)` | `Effect.catchCause(handler)` | +| `Effect.zipRight(next)` | `Effect.andThen(next)` | +| `Effect.async(cb => ...)` | `Effect.callback(resume => ...)` — rename param `cb` → `resume` | +| `Effect.andThen(eff, _ => plainValue)` | `Effect.map(eff, _ => plainValue)` — `Effect.andThen` in v4 only accepts Effect-returning functions, not plain values | +| `Effect.mapError(option, () => error)` | `Effect.flatMap(effect, Option.match({ onNone: () => Effect.fail(error), onSome: Effect.succeed }))` — `Effect.mapError` no longer has polymorphic overloads for Option | ## Either → Result @@ -77,11 +89,18 @@ class MyService extends ServiceMap.Service()("MyService", { | `r.left` | `r.failure` | | `r.right` | `r.success` | +## Layer API + +| v3 | v4 | +|---|---| +| `Layer.scoped(tag, scopedEffect)` | `Layer.effect(tag, effect)` — `Layer.scoped` renamed to `Layer.effect`. Scope is automatically excluded from R. | + ## Config API | v3 | v4 | |---|---| | `Config.hashMap(Config.string(), "name")` | `Config.schema(Config.Record(Schema.String, Schema.String), "name")` — reads sub-keys (e.g. `NAME__key=val`) | +| `Config.nested("prefix")` piped on another Config | **Removed** — use explicit key prefixes, e.g. `Config.string("prefix/key")` instead of `Config.string("key").pipe(Config.nested("prefix"))` | ## Removed Modules @@ -98,6 +117,56 @@ class MyService extends ServiceMap.Service()("MyService", { |---|---| | `ServiceMap.unsafeGet(map, tag)` | `ServiceMap.getUnsafe(map, tag)` | | `Chunk.unsafeGet(chunk, i)` | `Chunk.getUnsafe(chunk, i)` | +| `FiberSet.unsafeAdd(set, fiber)` | `FiberSet.addUnsafe(set, fiber)` | +| `RequestResolver.makeBatched(fn)` | `RequestResolver.make(fn)` (same API, just renamed) | +| `Array.isNonEmptyReadonlyArray(arr)` | `Array.isReadonlyArrayNonEmpty(arr)` | +| `Array.chunk_(arr, n)` | `Array.chunksOf(arr, n)` | +| `Equivalence.string` | `Equivalence.String` (capitalized, like `Order.String`) | +| `Predicate.isNotNullable` | No direct equivalent — use `Predicate.isString` when filtering `string \| undefined` | + +## Fiber API + +| v3 | v4 | +|---|---| +| `Fiber.RuntimeFiber` | `Fiber.Fiber` — `RuntimeFiber` namespace removed, use plain `Fiber` | + +## Context / ServiceMap + +| v3 | v4 | +|---|---| +| `Context.Context` (as type for service context) | `ServiceMap.ServiceMap` | +| `Context.empty()` | `ServiceMap.empty()` | +| `Context.TagMakeId("Tag", makeEffect)()` — creates class with `toLayerScoped()`, `use()`, `pipe()` | `ServiceMap.Service()("Tag", { make: makeEffect })` — auto-generates `Default` layer, use `Effect.andThen(this, fn)` in place of `this.use(fn)`, `Layer.scoped(this, make)` in place of `this.toLayerScoped()` | +| `Effect.gen(function*() { return yield* MyReference })` — unwrapping a Reference/Service into an Effect | `MyReference.asEffect()` — use `.asEffect()` for turning a Reference or Service tag into an Effect | +| `class MyRef extends Context.Reference()("key", { defaultValue })` — class-based Reference with `static readonly layer` | **Keep the class pattern** — `effect-app` exports a custom `Context.Reference` that re-adds the curried `()("key", { defaultValue })` overload. `ServiceMap.Reference` in vanilla v4 is not curried, but `effect-app/Context.Reference` supports both the direct form `Context.Reference("key", { defaultValue })` and the class form `class X extends Context.Reference()("key", { defaultValue }) { static readonly layer = Layer.effect(this, make) }`. Use `.asEffect()` to get an Effect from the reference. | + +Example migration for `Context.TagMakeId`: +```ts +// v3 +class MainFiberSet extends Context.TagMakeId("MainFiberSet", make)() { + static readonly Live = this.toLayerScoped() + static readonly run = (self: Effect.Effect) => this.use((_) => _.run(self)) +} + +// v4 +class MainFiberSet extends ServiceMap.Service()("MainFiberSet", { make }) { + static readonly Live = Layer.scoped(this, make) + static readonly run = (self: Effect.Effect) => Effect.andThen(this, (_) => _.run(self)) +} +``` + +## PubSub + +| v3 | v4 | +|---|---| +| `pubsub.publish(msg)` (method call) | `PubSub.publish(pubsub, msg)` (module function — no instance method) | + +## RPC (from `@effect/rpc` → `effect/unstable/rpc`) + +| v3 | v4 | +|---|---| +| `Rpc.fromTaggedRequest(MyTaggedRequestClass)` | `Rpc.make(resource._tag, { payload: resource, success: resource.success, error: resource.failure })` | +| `Rpc.make(tag).pipe(Rpc.annotateContext(...))` | `.annotate(tag, value)` method still exists on Rpc | ## Order Module diff --git a/task/human_findings.md b/task/human_findings.md new file mode 100644 index 000000000..5a783fd09 --- /dev/null +++ b/task/human_findings.md @@ -0,0 +1,24 @@ +- it looks at node_modules d.ts files instead of repos +- it replaces `Effect.zipRight` with `Effect.andThen` - why? +- what happened to `Config.nested`, why is it replacing with e.g `cups/server` +- Option.match replacement of embedding an Option in Effect, maybe use `.asEffect()`, instead of; + ```ts + repo.find(id).pipe( + Effect.flatMap( + Option.match({ + onNone: () => Effect.fail(new NotFoundError({ type: repo.itemType, id })), + onSome: Effect.succeed + }) + ) + ) + ``` +- we can remove `override pipe` +- it's not aware of `asEffect()`: + ```ts + Effect + .gen(function*() { + return yield* ContextMapContainer + }) + ``` +- it's removing `captureStackTrace: false` from EFfect.withSpan? +- it's getting confused by multiple effect versions installed, make sure to update all to v4 first and get rid of old patches. From e2e0a2e92c69d4d0104f72413214f1129aa59adf Mon Sep 17 00:00:00 2001 From: Patrick Roza Date: Tue, 24 Feb 2026 10:29:33 +0100 Subject: [PATCH 45/45] updates --- AGENTS.md | 4 ++-- packages/cli/package.json | 3 ++- packages/effect-app/package.json | 3 ++- packages/eslint-codegen-model/package.json | 3 ++- packages/infra/package.json | 3 ++- packages/vue/package.json | 3 ++- repos/effect | 2 +- task/Migrate_to_Effect_v4.md | 13 +++++++------ 8 files changed, 20 insertions(+), 14 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 90f41cdf9..eb3902919 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,7 +22,7 @@ This is the Effect App library repository, focusing on functional programming pa - Run `pnpm lint-fix` after editing files -- Run type checking: `pnpm build` +- Run type checking: `pnpm check` - If type checking continues to fail, run `pnpm clean` to clear caches, then re-run `pnpm check` @@ -30,7 +30,7 @@ This is the Effect App library repository, focusing on functional programming pa #### Migrations - Run `pnpm eslint fix ./src/` inside the package root after editing files -- Run type checking: `pnpm tsc --noEmit ./src/` inside the package root after editing files +- Run type checking: `pnpm check` inside the package root after editing files - If type checking continues to fail, run `pnpm clean` to clear caches, then re-run `pnpm tsc ./src/` diff --git a/packages/cli/package.json b/packages/cli/package.json index 5e81cd765..318e6fba4 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -54,7 +54,8 @@ }, "scripts": { "watch": "pnpm build:tsc -w", - "build:tsc": "pnpm clean-dist && tsc --build", + "build:tsc": "pnpm clean-dist && pnpm check", + "check": "tsc --build", "build": "pnpm build:tsc", "watch2": "pnpm clean-dist && NODE_OPTIONS=--max-old-space-size=6144 tsc -w", "clean": "rm -rf dist", diff --git a/packages/effect-app/package.json b/packages/effect-app/package.json index eaaa51511..14366ff2b 100644 --- a/packages/effect-app/package.json +++ b/packages/effect-app/package.json @@ -278,7 +278,8 @@ }, "scripts": { "watch": "pnpm build:tsc -w", - "build:tsc": "pnpm clean-dist && effect-app-cli packagejson && tsc --build", + "build:tsc": "pnpm clean-dist && effect-app-cli packagejson && pnpm check", + "check": "tsc --build", "build:tsc-src": "pnpm clean-dist && effect-app-cli packagejson tsc --build ./tsconfig.src.json", "build:src": "pnpm build:tsc-src", "build": "pnpm build:tsc", diff --git a/packages/eslint-codegen-model/package.json b/packages/eslint-codegen-model/package.json index d4b78df8e..627c1a314 100644 --- a/packages/eslint-codegen-model/package.json +++ b/packages/eslint-codegen-model/package.json @@ -5,7 +5,8 @@ "version": "1.47.0", "scripts": { "watch": "pnpm build:tsc -w", - "build:tsc": "pnpm clean-dist && tsc --build", + "build:tsc": "pnpm clean-dist && pnpm check", + "check": "tsc --build", "build": "tsc", "circular": "madge --circular --ts-config ./tsconfig.json --extensions ts ./src", "ncu": "ncu", diff --git a/packages/infra/package.json b/packages/infra/package.json index bd37a8e82..3a4f6eead 100644 --- a/packages/infra/package.json +++ b/packages/infra/package.json @@ -381,7 +381,8 @@ }, "scripts": { "watch": "pnpm build:tsc -w", - "build:tsc": "pnpm clean-dist && effect-app-cli packagejson tsc --build", + "build:tsc": "pnpm clean-dist && effect-app-cli packagejson pnpm check", + "check": "tsc --build", "build": "pnpm build:tsc", "watch2": "pnpm clean-dist && NODE_OPTIONS=--max-old-space-size=6144 tsc -w", "clean": "rm -rf dist", diff --git a/packages/vue/package.json b/packages/vue/package.json index 299fec6ba..ee0477252 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -112,7 +112,8 @@ }, "scripts": { "watch": "pnpm build:tsc -w", - "build:tsc": "pnpm clean-dist && effect-app-cli packagejson tsc --build", + "build:tsc": "pnpm clean-dist && effect-app-cli packagejson pnpm check", + "check": "tsc --build", "build": "pnpm build:tsc", "watch2": "pnpm clean-dist && NODE_OPTIONS=--max-old-space-size=6144 tsc -w", "clean": "rm -rf dist", diff --git a/repos/effect b/repos/effect index 4d97a61a1..82996bce8 160000 --- a/repos/effect +++ b/repos/effect @@ -1 +1 @@ -Subproject commit 4d97a61a15b9dd6a0eece65b8f0c035e16d42ada +Subproject commit 82996bce8debffcb44feb98bb862cf2662bd56b7 diff --git a/task/Migrate_to_Effect_v4.md b/task/Migrate_to_Effect_v4.md index d09f294bf..459881471 100644 --- a/task/Migrate_to_Effect_v4.md +++ b/task/Migrate_to_Effect_v4.md @@ -1,7 +1,7 @@ # Migration -Right now we are using Effect v3 (/repos/effect) -The task is about migrating to Effect v4 (/repos/effect-smol) +Right now we are using Effect v3 (repos/effect) +The task is about migrating to Effect v4 (repos/effect-smol) There are migration guides: - Announcement: https://effect.website/blog/releases/effect/40-beta/ @@ -29,10 +29,11 @@ Commit every task you complete for every step. - Save all conversion findings in a `task/findings.md` file to speed up future migrations. Read this file for every step! - Never replace any function argument type with `any` - Never cast to `any` as a "fix" (`(s as any)`)! nor recasting via `any` e.g `as any as S.Schema`. or `unknown`: e.g `as unknown as S.Schema`. Maybe you first need to fix other files. + - Consult the migration guides instead of making up assumptions. e.g `Schema` is now `Codec` - Prioritise first fixing files that are dependencies of others (via direct or indirect imports). - Migrate and fix files in dependency order - + ## Process - Always consult `findings.md` to help with migration or to fix build errors. @@ -59,7 +60,7 @@ We start with an as close as possible 1:1 conversion. - detect naming patterns we adopted from effect v3 in our libraries, and change them to match v4 naming patterns. - general refactorings and improvementsks -## Concerns +## Hints ### `Effect.Service` migration to `ServiceMap.Service` @@ -89,7 +90,7 @@ class GHGistService extends ServiceMap.Service()("GHGistService", ## Context -- The effect repo is located inside `repos/effect` -- The effect-smol repo is located inside `repos/effect-smol` +- The effect source code repository is located inside `repos/effect` +- The effect-smol source code repository is located inside `repos/effect-smol` All repos can be kept uptodate with `git submodule foreach git pull origin main` and `git submodule foreach pnpm i`.